#include "../cg/cg2100p.h"          // Campi maschere partite e pagamenti

#ifdef __EXTRA__
#include "../cg/cgsaldac.h"
#include "sc0100p.h"
#else
#include "cg2100.h"           // Campi maschere prima nota
#include "cg2102.h"           // Applicazione di prima nota
#endif

#include "cg2107.h"

#include <colors.h>           // Colori righe
#include <defmask.h>          // Serve per DLG_DELREC
#include <msksheet.h>
#include <relation.h>

#include <causali.h>          // Archivio causali

///////////////////////////////////////////////////////////
// Definizione Maschera partite
///////////////////////////////////////////////////////////

class TGame_mask : public TMask
{                       
  TBill _conto;                // Conto fisso del cliente/fornitore
  long _numreg;                // Numero movimento contabile
  int  _numrig;                // Riga contabile corrente (prima = 1!)
  TImporto _importo;           // Importo su riga contabile
  TImporto _residuo, _resval;  // Residuo della riga contabile
  int _riga_partite;           // Riga corrente delle partite
  bool _changed;               // Flag di modifica partite
  bool _valfirst;              // La colonna della valuta precede le lire
  bool _ignore_next_select;    // Flag per ignorare la select(0) iniziale sulle partite
  
protected:
  static bool annopart_handler(TMask_field& f, KEY k);
  static bool numpart_handler(TMask_field& f, KEY k);
  static bool partite_notify(TSheet_field& partite, int r, KEY k);
  static bool scadenze_notify(TSheet_field& partite, int r, KEY k);
  static bool show_all_handler(TMask_field& f, KEY k);
  static bool sort_part_handler(TMask_field& f, KEY k);
  static bool edit_scadenza_handler(TMask_field& f, KEY k);
  static bool nuovo_handler(TMask_field& f, KEY k);
  static bool cambio_handler(TMask_field& f, KEY k);

  void add_importo(TToken_string& s, const TImporto& i, const char* val = NULL, int pos = -1);
  void add_descrizione(TToken_string& s, const TRiga_partite& riga, int pos = -1);
  TImporto get_importo(TToken_string& s, int pos) const;
                      
  TToken_string& add_colored_row(TSheet_field& sheet, char type) const;

  void sort_partite();
  void fill_partite();
  
  void aggiorna_residuo();

  int update_partita(const TRectype& game, const TImporto& s, const TImporto& d,
                     const TImporto& p, const TImporto& i, int prow);
  int update_partita(const TPartita& game, int prow);
  int update_partita(const TRectype& game, int prow);
  bool partita_aperta(const TRectype& part) const;

  void update_saldo_clifo();
  int nuova_riga(TPartita& partita, tipo_movimento tm) const;
  int nuovo_pagamento(TPartita& partita, int nriga, int rata, tipo_movimento tm) const;
  bool edit_pagamento(TPartita& p, int nriga, int nrata, int nrigp) const;

  char calcola_sezione(tipo_movimento tm = tm_pagamento) const;
  long number_distance(const char* key, const char* num) const;
  bool same_number(const char* key, const char* num) const;
  void append_conto(TString& s) const;
  
#ifdef __EXTRA__  
  bool edit_fattura(TPartita& p, int nriga);
  bool prima_nota(const long nreg);
#endif

  const TRiga_partite* cerca_prima_riga() const;
  void aggiorna_valuta(const TValuta& val);
  
public:
  TSheet_field& partite() const { return sfield(P_PARTITE); }
  TSheet_field& scadenze() const { return sfield(P_SCADENZE); }
  const TBill& conto() const { return _conto; }
  
  const TImporto& residuo(bool val) const { return val ? _resval : _residuo; }
  bool changed() const { return _changed; }

  void init(const TBill& bill, long numreg, int riga);
  TGame_mask(const TBill& bill, long numreg, int riga);
  virtual ~TGame_mask() {}
};



///////////////////////////////////////////////////////////
// Maschera pagamenti
///////////////////////////////////////////////////////////

class TPay_mask : public TMask
{ 
  TDate _datadoc, _datarat;
  real _da_pagare, _pagabile;
  const TGame_mask* _parent;
  
  tipo_movimento _tipomov;
  
  bool _assigned;
  bool _can_solder;
  bool _swap_ritsoc;
  
protected:
  static bool importo_handler(TMask_field& f, KEY k);
  static bool importolire_handler(TMask_field& f, KEY k);
  static bool saldo_handler(TMask_field& f, KEY k);
  static bool datapag_handler(TMask_field& f, KEY k);
  static bool conto_handler(TMask_field& f, KEY k);
  static bool descr_handler(TMask_field& f, KEY k);
  void gioca_cambi(int force = 0x0);
  
#ifdef __EXTRA__  
  static bool datareg_handler(TMask_field& f, KEY k);
  static bool datadoc_handler(TMask_field& f, KEY k);
  static bool numdoc_handler(TMask_field& f, KEY k);
  static bool sezione_handler(TMask_field& f, KEY k);
#endif  

public:
  void set_pag(const TRectype& oldpag, const TRiga_scadenze& scad, const TImporto& importo);
  void get_pag(TRectype& oldpag, TRectype& somma) const;
  
  bool assigned() const { return _assigned; }
  bool unassigned() const { return !_assigned; }
  tipo_movimento tipo() const { return _tipomov; }
  
  void attiva_valuta(bool on);

  void init(const TGame_mask* parent, int mode);
  TPay_mask(const TGame_mask* parent, int mode);
  virtual ~TPay_mask();
};

#ifdef __EXTRA__         
const char* const PAYMASK = "sc0100b";
#else
const char* const PAYMASK = "cg2100s";
#endif    

void TPay_mask::init(const TGame_mask* parent, int mod)
{
  _parent = parent;
  set_mode(mod);
  enable(DLG_DELREC, edit_mode());
  
#ifdef __EXTRA__
  xvtil_statbar_set(mod == MODE_MOD ? TR("Modifica") : TR("Inserimento"), TRUE);

  hide(E_CODPAG); hide(E_DESPAG);  
  set_handler(E_DATAREG, datareg_handler);
  set_handler(E_DATADOC, datadoc_handler);
  set_handler(E_NUMDOC, numdoc_handler);
  set_handler(E_SEZIONE, sezione_handler);

  set_handler(E_TOTALE, TSaldaconto_app::totale_handler);
  if (app().gestione_valuta())
  {            
    show(-3);  
    set_handler(E_TOTDOCVAL,  TSaldaconto_app::totval_handler);
    set_handler(E_VALUTA,     TSaldaconto_app::valuta_handler);
    set_handler(E_DATACAMBIO, TSaldaconto_app::datacambio_handler);
    set_handler(E_CAMBIO,     TSaldaconto_app::cambio_handler);
  }
  else
    hide(-3);  

  hide(S_RESIDUOPAG);
  hide(-2); // I campi relativi alla contropartita non vengono gestiti
  hide(E_IMPOSTE);
                                                                      
  set_handler(E_DESCR, descr_handler);
  set_handler(S_DESCAGG, descr_handler);
                                                                      
  const char a = TPartita::allineamento_richiesto(parent->conto().tipo());
  field(E_NUMRIF).set_justify(a == 'R');

  disable(E_ANNORIF);
  disable(E_NUMRIF);
#endif
}

TPay_mask::TPay_mask(const TGame_mask* parent, int mod) 
         : TMask(PAYMASK)
{ 
  init(parent, mod);
} 

TPay_mask::~TPay_mask() 
{ }

void TPay_mask::attiva_valuta(bool in_valuta)
{
  if (in_valuta)
  {
    set_handler(S_IMPORTOVAL, importo_handler); 
    set_handler(S_IMPORTO, importolire_handler); 
  }
  else  
    set_handler(S_IMPORTO, importo_handler); 
    
  enable(S_RITENUTE, !in_valuta);       // dis/abilita ritenute
  enable(S_RITSOC, !in_valuta);       
  enable(S_IMPORTOVAL, in_valuta);
  
  if (in_valuta) 
  {
    reset(S_RITENUTE);
    reset(S_RITSOC);
  }
  else
  {
    reset(S_IMPORTOVAL_SCAD);
    reset(S_IMPORTOVAL);
  }  
}

void TPay_mask::set_pag(const TRectype& oldpag, const TRiga_scadenze& scad, 
                        const TImporto& residuo)
{                   
  const TPartita& p = scad.partita();
  const int nrigp = oldpag.get_int(PAGSCA_NRIGP);
  const TRiga_partite& sum = p.riga(nrigp);
  _assigned = oldpag.get_int(PAGSCA_NRIGA) != TPartita::UNASSIGNED;
  _tipomov = (tipo_movimento)sum.get_int(PART_TIPOMOV);
  _swap_ritsoc = sum.sezione() != sum.sezione_ritsoc();
  
  const TRiga_partite& fatt = _assigned ? scad.riga() : sum;

  TRelation rel(LF_PAGSCA);                // Working relation
  rel.add(LF_PARTITE, "ANNO==ANNO|NUMPART==NUMPART");
  rel.curr() = oldpag;
  rel.curr(LF_PARTITE) = sum;                                           
  set(S_CODVAL, fatt.get(PART_CODVAL));   // Importante settare la valuta per i TCurrency!  CM500433
  autoload(rel);                          // Load current record on mask
  
  TMask_field& group = field(S_RATA);
  TString prompt(80);
  
  const TBill& k = p.conto();
  switch (k.tipo())
  {
  case 'C': prompt << TR("Cliente"); break;
  case 'F': prompt << TR("Fornitore"); break;
  default : prompt << TR("Conto ") << k.gruppo() << ' ' << k.conto(); break;
  }
  prompt << ' ' << k.sottoconto() << ' ';
  prompt << TR("Partita:") << p.anno() << ' ' << p.numero()
         << TR(" Riga:") << oldpag.get_int(PAGSCA_NRIGA) 
         << TR(" Rata:") << oldpag.get_int(PAGSCA_NRATA);
  if (assigned())       
    prompt << TR(" del ") << scad.get_date(SCAD_DATASCAD).string();
#ifndef __EXTRA__    
  else
    prompt << TR(" del ") << sum.get_date(PART_DATAPAG).string();
#endif    
  group.set_prompt(prompt);

  set(S_NUMDOC,  fatt.get(PART_NUMDOC));             // Numero documento
  set(S_DATADOC, fatt.get(PART_DATADOC));            // Data documento
  set(S_NUMPROT, fatt.get(PART_PROTIVA));            // Protocollo IVA

  TString desfat = fatt.get(PART_DESCR);             // Descrizione fattura
  if (desfat.empty())                                // Se e' vuota ...
  {
    desfat = fatt.get(PART_CODCAUS);
    desfat = cache().get(LF_CAUSALI, desfat, CAU_DESCR); // ... usa descrizione causale
  }
  set(S_DESCR, desfat);                              

  bool in_valuta = fatt.in_valuta();
#ifdef __EXTRA__
  const bool prima_riga = p.first() == p.last();
  if (!in_valuta && prima_riga)
    in_valuta = app().gestione_valuta();
  enable(E_VALUTA, prima_riga); // La valuta puo' essere cambiata solo sulle partite nuove
#else
  set(S_CODVAL, fatt.codice_valuta());  // Copia in maschera la valuta
#endif  
  show(-3, in_valuta);          // Visualizza campi relativi alla valuta
  attiva_valuta(in_valuta);     // Attiva campi e handlers relativi alla valuta
  
  const char sez_fat = fatt.sezione();
  set(S_SEZIONE_SCAD, sez_fat == 'A' ? "A" : "D"); // Sezione della riga
  if (assigned())
  {
    set(S_IMPORTO_SCAD, scad.get(SCAD_IMPORTO));         // Importo della rata
    if (in_valuta)
      set(S_IMPORTOVAL_SCAD, scad.get(SCAD_IMPORTOVAL)); // Importo in valuta 
    TImporto res_rat = scad.residuo(in_valuta);  
    res_rat.normalize(sez_fat);  
    _da_pagare = res_rat.valore();       // Calcola residuo in valuta

    TEdit_field& resrat = efield(S_RESIDUORATA);
    resrat.reset_driver();
    if (in_valuta) 
      resrat.add_driver(S_CODVAL);

    if (get(S_SALDOACC)[0] != 'S')
      resrat.set(_da_pagare.string());
  }
  else                
  {
    hide(S_RESIDUORATA);        // Se non assegnato nascondi residuo rata
    _da_pagare = ZERO;
  }
  
  set_handler(S_SALDOACC, saldo_handler);
  
  real oldimp = oldpag.get_real(in_valuta ? PAGSCA_IMPORTOVAL : PAGSCA_IMPORTO);   
  if (!in_valuta)
  {
    oldimp += oldpag.get_real(PAGSCA_RITENUTE);
    
    if (_swap_ritsoc)
      oldimp -= oldpag.get_real(PAGSCA_RITSOC);
    else
      oldimp += oldpag.get_real(PAGSCA_RITSOC);
  }
  
  // Ricorda l'importo da pagare
  _da_pagare += oldimp;
  
#ifndef __EXTRA__
  _pagabile = _parent->residuo(in_valuta).valore();

  TEdit_field& respag = efield(S_RESIDUOPAG);
  respag.reset_driver();
  if (in_valuta)
    respag.add_driver(S_CODVAL);
  respag.set(_pagabile.string());
  
  _pagabile += oldimp;
#endif
  
  // Il flag di saldo/acconto e' attivo solo se non ci sono acconti, cioe':
  // pagamento non assegnato o con data documento antecedente quella della fattura
  _can_solder = _assigned;
  if (_can_solder)
  {                            
    const tipo_movimento tm = sum.tipo();
    _can_solder = !(tm == tm_nota_credito || tm == tm_insoluto);
/*  if (_can_solder)
    {
      const TDate datasca(fatt.get(PART_DATADOC)); 
      const TDate datapag(sum.get(PART_DATADOC));
      _can_solder = datapag >= datasca;
    }  */
  }

  // Mostra saldo solo se non e' ne' un acconto, ne' una nota di credito
  enable(S_SALDOACC, _can_solder);  

#ifdef __EXTRA__                          
  enable(E_SEZIONE, oldpag.get_char(PAGSCA_ACCSAL) != 'S');
#else
  set_handler(S_GRUPPO, conto_handler);
  set_handler(S_CONTO, conto_handler);
  const bool mostra_conto = !sum.is_nota_credito() && app().curr_mask().get_real(F_TOTALE) != ZERO;
  show(-2, mostra_conto);       // mostra/nasconde conto contropartita
  if (!mostra_conto)
    reset(-2);
#endif  

// Gestione data-pagamento: non puo' precedere la data del documento
  _datadoc = sum.get_date(PART_DATADOC);
  _datarat = _assigned ? scad.get(SCAD_DATASCAD) : EMPTY_STRING;
  set_handler(S_DATAPAG, datapag_handler); 

  const bool mostra_ritenute = !sum.is_nota_credito();
  show(S_RITENUTE, mostra_ritenute);
  show(S_RITSOC, mostra_ritenute);
} 

void TPay_mask::get_pag(TRectype& newpag, TRectype& sum) const
{
  TRelation rel(LF_PAGSCA);                // Working relation
  rel.add(LF_PARTITE, "ANNO=ANNO|NUMPART=NUMPART");
  rel.curr() = newpag;       
  rel.curr(LF_PARTITE) = sum;
  autosave(rel);                          // Load current record from mask
  newpag = rel.curr();
  sum = rel.curr(LF_PARTITE);
}     

// Riempie i campi valuta a zero in base agli altri
void TPay_mask::gioca_cambi(int force)
{               
  const real totale = get_real(S_IMPORTO);
  const real totval = get_real(S_IMPORTOVAL);

#ifdef __EXTRA__
  const TValuta cambio(*this, E_VALUTA, E_DATACAMBIO, E_CAMBIO, E_CONTROEURO);
#else  
  const TValuta cambio(*_parent, P_VALUTA, P_DATACAMBIO, P_CAMBIO, P_CONTROEURO);
#endif  

  if ( (force == 0x1 || totale.is_zero()) && !(totval.is_zero() || cambio.in_lire()) )
  {
    const real new_totale = cambio.val2lit(totval);
    if (new_totale != totale)
      set(S_IMPORTO, new_totale, TRUE);
  } 
  
  if ( (force == 0x2 || totval.is_zero()) && !(totale.is_zero() || cambio.in_lire()))
  {
    const real new_totval = cambio.lit2val(totale);
    if (new_totval != totval)
      set(S_IMPORTOVAL, new_totval, TRUE);
  } 

#ifdef __EXTRA__  
  if ( (force == 0x4 || cambio.in_lire()) && !(totale.is_zero() || totval.is_zero()))
  {
    real new_cambio = totale / totval; new_cambio.round(5);
    if (new_cambio != cambio.cambio())
      set(E_CAMBIO, new_cambio, TRUE);
  } 
#endif  
}


bool TPay_mask::importo_handler(TMask_field& f, KEY k)
{
  TPay_mask& m = (TPay_mask&)f.mask();         

  if (k == K_F8)
  {        
    real imp;
#ifdef __EXTRA__
    const bool in_valuta = m.field(S_IMPORTOVAL).active();
    m._pagabile = m.get_real(in_valuta ? E_TOTDOCVAL : E_TOTALE);
    if (m._assigned)
      imp = fnc_min(m._da_pagare, m._pagabile);
    else
      imp = m._pagabile;
#else
    if (m.field(S_RESIDUORATA).shown() && m.field(S_RESIDUOPAG).shown())
      imp = fnc_min(m._da_pagare, m._pagabile);
    else
      imp = m.field(S_RESIDUORATA).shown() ? m._da_pagare : m._pagabile;
#endif
      
    if (m.field(S_RITENUTE).active())
      imp -= m.get_real(S_RITENUTE);
    if (m.field(S_RITSOC).active())
    {
      if (m._swap_ritsoc)
        imp += m.get_real(S_RITSOC);
      else
        imp -= m.get_real(S_RITSOC);
    }
    
    f.set(imp.string());           
    k = K_TAB;
  }           
  
  if (k == K_TAB && (f.focusdirty() || !m.is_running()))
  {            
    const bool in_valuta = m.field(S_IMPORTOVAL).active();
    if (in_valuta)
      m.gioca_cambi();
  
    real i(f.get());
    if (m.field(S_RITENUTE).active())
      i += m.get_real(S_RITENUTE);
    if (m.field(S_RITSOC).active())
    {
      if (m._swap_ritsoc)
        i -= m.get_real(S_RITSOC);
      else
        i += m.get_real(S_RITSOC);
    }
    
    TMask_field& sa = m.field(S_SALDOACC);
    TMaskmode mod = (TMaskmode)m.mode();
    if (m._can_solder && (mod == NO_MODE || mod == MODE_INS))
    {                      
      if (i >= m._da_pagare)
        sa.set("S");
    }  
    
    if (sa.get()[0] != 'S')
    {
      real residuo = m._da_pagare;
      if (m.tipo() == tm_insoluto)
        residuo += i;
      else  
        residuo -= i;
      m.set(S_RESIDUORATA, residuo);
    }
    else
      m.reset(S_RESIDUORATA);
      
    const real residuopag(m._pagabile - i);
    m.set(S_RESIDUOPAG, residuopag);
  } 
  
  return TRUE;
}

bool TPay_mask::importolire_handler(TMask_field& f, KEY k)
{               
  TPay_mask& m = (TPay_mask&)f.mask();         

  if (k == K_F8)
  {
#ifdef __EXTRA__
    if (m.unassigned())
      f.set(m.get(E_TOTALE));
    else  
#endif  
    m.send_key(k, S_IMPORTOVAL);
  }
    
  if (f.to_check(k))
    m.gioca_cambi();
  return TRUE;
}

bool TPay_mask::saldo_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {           
    TMask& m = f.mask();
    m.set_mode(MODE_QUERY);
    const bool valuta = m.field(S_IMPORTOVAL).active();
    TMask_field& imp =  m.field(valuta ? S_IMPORTOVAL : S_IMPORTO);
    imp.set_dirty();
    imp.on_hit();
    m.set_mode(NO_MODE);
  }
  return TRUE;
}

bool TPay_mask::datapag_handler(TMask_field& f, KEY k)
{   
  if (f.to_check(k) || k == K_ENTER)           
  {
    const TDate datapag(f.get());
    TPay_mask& m = (TPay_mask&)f.mask();         
    if (datapag < m._datadoc)
      return f.error_box(FR("La data del pagamento e' inferiore alla data del documento %s"), 
                         m._datadoc.string());
    if (datapag < m._datarat)
      warning_box(TR("Attenzione: la data del pagamento precede quella della rata"));
  }
  return TRUE;
}

bool TPay_mask::conto_handler(TMask_field& f, KEY k)
{   
  if (k == K_TAB && f.focusdirty())
  {                        
    TMask& m = f.mask();
    TBill conto; conto.get(m, S_GRUPPO, S_CONTO, S_SOTTOCONTO);
    const TString& desc = conto.descrizione();
    if (desc.empty()) 
      m.reset(S_SOTTOCONTO);
    m.set(S_DESCRCONTO, desc);
  }           
  return TRUE;
}

///////////////////////////////////////////////////////////
// Maschera gestione nuovo pagamento / fattura
///////////////////////////////////////////////////////////

class TNew_mask : public TMask
{        
  bool _allow_fatt;

protected:
  static bool tipomov_handler(TMask_field& f, KEY k);
              
public:
  void init(char tipocf, bool fatt, bool edit);

  TNew_mask(char tipocf, bool fatt, bool edit);
  virtual ~TNew_mask() {}
};                                                         

bool TNew_mask::tipomov_handler(TMask_field& f, KEY k)
{
  if (k == K_ENTER)
  {
    TNew_mask& m = (TNew_mask&)f.mask();
    if (!m._allow_fatt && f.get() == "1")
      return f.error_box(TR("Non e' possibile utilizzare una fattura come pagamento"));
  }
  
  return TRUE;
}

void TNew_mask::init(char tipocf, bool fatt, bool edit)
{                          
  _allow_fatt = fatt;

  TMask_field& tipomov = field(P_NUOVO);
#ifdef __EXTRA__
  tipomov.set_handler(tipomov_handler);
  tipomov.enable();
  tipomov.set(_allow_fatt ? "1" : "3");
  if (!_allow_fatt)
    set_caption(TR("Nuovo pagamento"));
  reset(P_NUMERO);
#else  
  tipomov.disable();
#endif  

  enable(P_ANNO,   edit);
  enable(P_NUMERO, edit);
  if (edit) first_focus(P_NUMERO);
  
  const char a = TPartita::allineamento_richiesto(tipocf);
  field(P_NUMERO).set_justify(a == 'R');
}

TNew_mask::TNew_mask(char tipocf, bool fatt, bool edit)
         : TMask("cg2100n")
{   
  init(tipocf, fatt, edit);
}         

///////////////////////////////////////////////////////////
// Maschera partite
///////////////////////////////////////////////////////////

void TGame_mask::append_conto(TString& s) const
{
  switch (conto().tipo()) 
  {
  case 'C':
    s << TR("Cliente"); break;
  case 'F':
    s << TR("Fornitore"); break;
  default:
    s << TR("Conto") << ' ' << conto().gruppo() << ' ' << conto().conto(); break;
  }    
  s << ' ' << conto().sottoconto();  
}

void TGame_mask::init(const TBill& bill, long numreg, int riga) 
{                 
  _conto = bill;
  _numreg = numreg; _numrig = riga;
  _changed = FALSE;

  TString descr(80);
  append_conto(descr);
  descr << ' ' << ((TBill&)_conto).descrizione();
  set(P_DESCR, descr);
              
#ifdef __EXTRA__  
  xvtil_statbar_set(TR("Estratto conto"), TRUE);

  disable(-3);     // Disabilita gestione valuta
  hide(P_RESIDUO);
  hide(P_RESIDUOVAL);
#else
  TValuta val;
  const TRiga_partite* row = cerca_prima_riga();
  if (row != NULL)
  {                                   
    val.get(*row);                               // Legge valuta standard dalla partita
    set(P_ANNO, row->get(PART_ANNO));            // Propone anno e partita
    set(P_NUMERO, row->get(PART_NUMPART));
  }
  else
  {
    TMask& cm = app().curr_mask();               // Legge valuta dal movimento      
    val.get(cm, SK_VALUTA, SK_DATACAMBIO, SK_CAMBIO, SK_CONTROEURO);
    
    if (cm.id2pos(F_ANNORIF) > 0)                // Se in testata c'e' l'anno di riferimento
    {
      set(P_ANNO, cm.get(F_ANNORIF));            // Propone anno e partita
      set(P_NUMERO, cm.get(F_NUMRIF));             
    }  
  }
  val.set(*this, P_VALUTA, P_DATACAMBIO, P_CAMBIO);
  enable(-3, val.in_valuta());
#endif  
  
  set_handler(P_ANNO,    annopart_handler);
  set_handler(P_NUMERO,  numpart_handler);
  set_handler(P_SHOWALL, show_all_handler);
  set_handler(P_SORT,    sort_part_handler);
  set_handler(P_NUOVO,   nuovo_handler);
  set_handler(P_CAMBIO,  cambio_handler);
  
  TSheet_field& games = partite();
  games.set_notify(partite_notify);
    
  scadenze().set_notify(scadenze_notify);
  scadenze().sheet_mask().set_handler(100, edit_scadenza_handler);
  
  fill_partite();                                     // Riempie sheet partite
}

TGame_mask::TGame_mask(const TBill& bill, long numreg, int riga) 
: TMask("cg2100p"), _valfirst(FALSE), _ignore_next_select(FALSE)
{     
  init(bill, numreg, riga);
}

///////////////////////////////////////////////////////////                          
// Handlers dei campi e della maschera principale
///////////////////////////////////////////////////////////                          

bool TGame_mask::annopart_handler(TMask_field& f, KEY k)
{
  if (k == K_TAB && f.focusdirty() && f.get().not_empty())
  {
    TMask_field& n = f.mask().field(P_NUMERO);
    n.set_dirty();
    numpart_handler(n, k);
  }
  return TRUE;
}

bool TGame_mask::numpart_handler(TMask_field& f, KEY k)
{
  if (k == K_TAB && f.focusdirty())
  {                  
    const TGame_mask& m = (const TGame_mask&)f.mask();
    const int anno = m.get_int(P_ANNO);        // Anno partita da cercare
    if (anno > 0)
    {
      const TString key = f.get();             // Numero partita da cercare
      int best_match = 0;                      // Partita piu' somigliante
      long min_dist = 10000000L;               // Livello di somiglianza migliore
      
      TSheet_field& sheet = m.partite();
      for (int i = 0; i < sheet.items(); i++)
      {
        TToken_string& row = sheet.row(i);
        if (anno == row.get_int(0))            // Se corrisponde l'anno e ...
        {        
          const long dist = m.number_distance(key, row.get());
          if (i == 0 || dist < min_dist)
          {
            min_dist = dist;
            best_match = i;
            if (dist == 0L)
              break;
          }
        }  
      }
      sheet.post_select(best_match);
    }
  }
  return TRUE;
}

bool TGame_mask::show_all_handler(TMask_field& f, KEY k)
{
  TGame_mask& gm = (TGame_mask&)f.mask();
  if (k == K_SPACE && gm.is_running())
  {
    gm.fill_partite();
  } 
  return TRUE;
}

bool TGame_mask::sort_part_handler(TMask_field& f, KEY k)
{
  TGame_mask& gm = (TGame_mask&)f.mask();
  if (k == K_SPACE && gm.is_running())
    gm.sort_partite();
  return TRUE;
}

bool TGame_mask::cambio_handler(TMask_field& f, KEY k)
{
  if (k == K_TAB && f.focusdirty())
  {
    TGame_mask& gm = (TGame_mask&)f.mask();
    const bool needed = app().partite().mov2rig(gm._numreg, gm._numrig) > 0;
    if (needed && yesno_box(TR("Aggiornare il cambio di tutti i pagamenti effettuati in questa registrazione?")))
    {                                     
      const TValuta val(gm, P_VALUTA, P_DATACAMBIO, P_CAMBIO, P_CONTROEURO);
      gm.aggiorna_valuta(val);
    }  
    gm.aggiorna_residuo();
  }
  return TRUE;
}

///////////////////////////////////////////////////////////                          
// Metodi dei campi e della maschera principale
///////////////////////////////////////////////////////////                          

// Aggiorna il campo con il residuo da spendere sui pagamenti
void TGame_mask::aggiorna_residuo()
{                            
#ifndef __EXTRA__
  _importo = app().get_cgs_imp(_numrig-1);                     // Importo sulla riga contabile
  _residuo = _importo;
  
  TPartite_array& giochi = app().partite();
  const TImporto speso = giochi.importo_speso(_numreg, _numrig); 
  _residuo -= speso;                                           // Residuo della riga

  const char sez = calcola_sezione();                          // Sezione di riferimento
  _residuo.normalize(sez);
  set(P_RESIDUO, _residuo.valore());
  
  const TValuta cambio(*this, P_VALUTA, P_DATACAMBIO, P_CAMBIO, P_CONTROEURO);
  if (cambio.in_valuta())
  {                                                                         
    // Importo della riga contabile senza differenza cambi
    _resval = _importo;
    const TImporto abb_diff = giochi.importo_speso(_numreg, _numrig, FALSE, 0x6);
    _resval -= abb_diff;
    cambio.lit2val(_resval);
    const TImporto spesoval = giochi.importo_speso(_numreg, _numrig, TRUE, 0x1);
    _resval -= spesoval;                                       // Residuo della riga
    _resval.normalize(sez);
    set(P_RESIDUOVAL, _resval.valore());
  }
  else
    reset(P_RESIDUOVAL);
#endif  
}

// Scandisce tutte le partite per cercare la prima del movimento corrente
const TRiga_partite* TGame_mask::cerca_prima_riga() const
{   
  const TRiga_partite* riga = app().partite().mov2rig(_numreg, _numrig);
  return riga;
}


void TGame_mask::aggiorna_valuta(const TValuta& val)
{          
  TPartite_array& pa = app().partite();
  for (TPartita* game = pa.first(); game; game = pa.next())
  {
    for (int r = game->last(); r > 0; r = game->pred(r))
    {
      const TRiga_partite& riga = game->riga(r);
      for (int s = riga.rate(); s > 0; s--)
      {
        const TRiga_scadenze& scad = riga.rata(s);
        for (int p = scad.last(); p > 0; p = scad.pred(p))
        {
          TRiga_partite& sum = game->riga(p);
          if (sum.get_long(PART_NREG) == _numreg && 
              sum.get_int(PART_NUMRIG) == _numrig)
          {     
            TRectype pag(scad.row(p));
            real imp(pag.get(PAGSCA_IMPORTOVAL));
            val.val2lit(imp);
            pag.put(PAGSCA_IMPORTO, imp);          // Converte in lire l'importo in valuta
#ifdef __EXTRA__
            game->modifica_pagamento(pag, val, TRUE);
#else                           
            app().notify_edit_pagamento(*game, pag, val);
#endif            
          }
        }
      }
    }
  }  
  fill_partite(); 
}

TToken_string& TGame_mask::add_colored_row(TSheet_field& sheet, char type) const
{    
  int r = sheet.insert(-1, FALSE, FALSE);
  COLOR back, fore;
  app().type2colors(type, back, fore);
  sheet.set_back_and_fore_color(back, fore, r);
  return sheet.row(r);
}

bool TGame_mask::partite_notify(TSheet_field& partite, int r, KEY k)
{
  TGame_mask& gm = (TGame_mask&)partite.mask();

  if (k == K_TAB)
  {                  
    if (gm._ignore_next_select)
    {
      gm._ignore_next_select = FALSE;
      return TRUE;
    }
  
    const bool changing_row = gm._riga_partite != r;
    if (!changing_row)
      return TRUE;
      
    TWait_cursor hourglass;  
      
    gm._riga_partite = r;
    
    TSheet_field& sheet = gm.scadenze();
    sheet.destroy(-1, FALSE);                               // Azzera righe

    TToken_string& row = partite.row(r);
    const int anno = row.get_int(0);                        // Anno partita
    const TString16 num = row.get();                        // Numero partita
    gm.set(P_ANNO, anno);                                   // Aggiorna campi di ricerca
    gm.set(P_NUMERO, num);
    
    TValuta prima_valuta;                                   // Codice prima valuta
      
    if (anno > 0)
    {
      const TBill& zio = gm.conto();                          // Conto cliente/fornitore
      
      TPartita* game = app().partite().exist(zio, anno, num); // Cerca la partita tra quelle editate
      const bool should_delete_game = (game == NULL);         // Ricorda di fare delete
      if (should_delete_game)                                 // Se non c'era ...
        game = new TPartita(zio, anno, num);                  // ... creane una temporanea
      
      TImporto tot_lit, tot_val;
      
      const int lastrow = game->last();
      
      if (lastrow > 0)
        prima_valuta.get(game->riga(lastrow));
      const bool in_valuta = prima_valuta.in_valuta();
      
      for (int ri = game->first(); ri <= lastrow; ri = game->succ(ri))
      {
        const TRiga_partite& riga = game->riga(ri);
        if (!riga.is_fattura())
          continue;

        TToken_string& riga_fattura = gm.add_colored_row(sheet, 'K');
        riga_fattura.add(ri);
        riga_fattura.add("");
        riga_fattura.add(riga.get(PART_DATADOC)); 
        riga_fattura.add("");     // Data scad
        gm.add_descrizione(riga_fattura, riga);
        gm.add_importo(riga_fattura, riga.importo(FALSE, 0x1));
        if (in_valuta)
          gm.add_importo(riga_fattura, riga.importo(TRUE, 0x1), prima_valuta.codice());
        else
          riga_fattura.add("");  
        riga_fattura.add(riga.get(PART_NREG));
        riga_fattura.add(riga.get(PART_DATAREG));
        riga_fattura.add(riga.get(PART_NUMDOC)); 
        riga_fattura.add(riga.get(PART_PROTIVA)); 
        riga_fattura.add(riga.get(PART_TIPOMOV)); 
        
        for (int ra = 1; ra <= riga.rate(); ra++)
        {
          const TRiga_scadenze& scad = riga.rata(ra);
            
          TToken_string& row = gm.add_colored_row(sheet, 'I');
          row = riga_fattura;
          row.add(ra, 1);                            
          row.add(scad.get(SCAD_DATASCAD), 3); 
          gm.add_importo(row, scad.importo(FALSE), NULL, 5);
          if (in_valuta)
            gm.add_importo(row, scad.importo(TRUE), prima_valuta.codice(), 6);
          
          const TString& descr = scad.get(SCAD_DESCR);   
          if (descr.not_empty())
            row.add(descr, 4);
          
          const bool blocked = scad.get_bool(SCAD_BLOCCATA);
          row.add(blocked ? "X" : " ", 13);
            
          const int lastp = scad.last();
          for (int pa = scad.first(); pa <= lastp; pa = scad.succ(pa))
          {
            const TRectype& pag = scad.row(pa);
            const TRiga_partite& sum = game->riga(pa);
            const char sez = sum.sezione(); 
            const long nreg = sum.get_long(PART_NREG);
            const int nrig = sum.get_int(PART_NUMRIG);
            const bool linked = nreg == gm._numreg && nrig == gm._numrig;
            TToken_string& row = gm.add_colored_row(sheet, linked ? 'X' : ' ');
            row.add(ri);
            row.add(ra);
            row.add(sum.get(PART_DATADOC)); 
            row.add(sum.get(PART_DATAPAG)); 
            gm.add_descrizione(row, sum);
            const TImporto imp(sez, pag.get_real(PAGSCA_IMPORTO));
            if (in_valuta)
            {
              gm.add_importo(row, imp);
              gm.add_importo(row, TImporto(sez, pag.get_real(PAGSCA_IMPORTOVAL)), prima_valuta.codice());
            }  
            else
            {
              gm.add_importo(row, imp);
              row.add("");
            }  
            row.add(nreg);
            row.add(sum.get(PART_DATAREG));
            row.add(sum.get(PART_NUMDOC)); 
            row.add(""); 
            row.add(sum.get(PART_TIPOMOV)); 
            row.add(pa);
            
            // Le ritenute non possono esistere in valuta
            if (!in_valuta)
            {
              const TImporto rit(sez, pag.get_real(PAGSCA_RITENUTE)); 
              if (!rit.is_zero())   
              {
                TToken_string& rrit = gm.add_colored_row(sheet, 'R');
                rrit.add(TR("Ritenute professionali"), 4); 
                gm.add_importo(rrit, rit, FALSE);
                rrit.add(sum.get(PART_TIPOMOV), 11);
              }
              
              const TImporto soc(sum.sezione_ritsoc(), pag.get_real(PAGSCA_RITSOC)); 
              if (!soc.is_zero())   
              {
                TToken_string& rrit = gm.add_colored_row(sheet, 'R');
                rrit.add(TR("Ritenute sociali"), 4); 
                gm.add_importo(rrit, soc, FALSE);
                rrit.add(sum.get(PART_TIPOMOV), 11);
              }
            }  

            // Gli abbuoni e le differenze cambio esistono solo se e' a saldo
            if (pag.get_char(PAGSCA_ACCSAL) == 'S')
            {
              const TImporto abb(sez, pag.get_real(PAGSCA_ABBUONI));
              if (!abb.is_zero())
              {
                const char tipo_abb = pag.get_char(PAGSCA_PASSATT);
                TToken_string& rabb = gm.add_colored_row(sheet, tipo_abb);
                rabb.add(tipo_abb == 'A' ? TR("Abbuoni attivi") : TR("Abbuoni passivi"), 4);
                if (in_valuta)
                {                   
                  TImporto abb_lit = abb;
                  prima_valuta.val2lit(abb_lit);
                  gm.add_importo(rabb, abb_lit);
                  gm.add_importo(rabb, abb, prima_valuta.codice());
                }  
                else  
                {
                  gm.add_importo(rabb, abb);
                  rabb.add("");
                }
                rabb.add(sum.get(PART_TIPOMOV), 11);
              }
              
              // Le differenze cambio possono esistere solo in valuta
              if (in_valuta)  
              {
                const TImporto diff(sez, pag.get_real(PAGSCA_DIFFCAM));
                if (!diff.is_zero())
                {
                  TToken_string& rdiff = gm.add_colored_row(sheet, 'C');
                  rdiff.add(TR("Differenza cambio"), 4);
                  gm.add_importo(rdiff, diff);
                  rdiff.add(sum.get(PART_TIPOMOV), 11);
                }
              }  
            
            }      // Il pagamento era a saldo
          }        // Fine ciclo sui pagamenti della rata
    
          TToken_string& rsal = gm.add_colored_row(sheet, 'S');
          rsal.add(TR("Saldo rata "), 4); rsal << ra;
          if (!scad.chiusa())
          {           
            TImporto sl = scad.residuo(false);
            gm.add_importo(rsal, sl);
            tot_lit += sl;
              
            if (in_valuta)
            { 
              sl = scad.residuo(true, 0xB);
              gm.add_importo(rsal, sl, prima_valuta.codice());
              tot_val += sl;
            }  
          }  
        }          // Fine ciclo sulle rate della fattura
      }
      
      TRecord_array& unas = game->unassigned();
      const int lastp = unas.last_row();
      for (int pa = unas.first_row(); pa <= lastp; pa = unas.succ_row(pa))
      {
        const TRectype& pag = unas.row(pa);
        const TRiga_partite& sum = game->riga(pa);
        const char sez = sum.sezione();
        const long nreg = sum.get_long(PART_NREG);
        const int nrig = sum.get_int(PART_NUMRIG);
        const bool linked = nreg == gm._numreg && nrig == gm._numrig;
        TToken_string& row = gm.add_colored_row(sheet, linked ? 'X' : ' ');
        row.add(pag.get(PAGSCA_NRIGA));
        row.add(pag.get(PAGSCA_NRATA));
        row.add(sum.get(PART_DATADOC)); 
        row.add(sum.get(PART_DATAPAG));
        gm.add_descrizione(row, sum);
        
        TImporto imp(sez, pag.get_real(PAGSCA_IMPORTO));
        gm.add_importo(row, imp);
        tot_lit += imp;
        
        const real& impval = pag.get_real(PAGSCA_IMPORTOVAL);
        if (!impval.is_zero())     
        { 
          imp.set(sum.sezione(), impval);
          imp.normalize();
          gm.add_importo(row, imp, prima_valuta.codice());
          tot_val += imp;
        }  
        else
          row.add("");  
        row.add(nreg);
        row.add(sum.get(PART_DATAREG));
        row.add(sum.get(PART_NUMDOC)); 
        row.add(""); 
        row.add(sum.get(PART_TIPOMOV)); 
        row.add(pa);
        
        const TImporto rit(sez, pag.get_real(PAGSCA_RITENUTE));
        if (!rit.is_zero())
        {
          TToken_string& row = gm.add_colored_row(sheet, 'R');
          row.add(TR("Ritenute professionali"), 4); 
          gm.add_importo(row, rit, FALSE);
          row.add(sum.get(PART_TIPOMOV), 11);
          tot_lit += rit;
        }

        const TImporto soc(sum.sezione_ritsoc(), pag.get_real(PAGSCA_RITSOC));
        if (!soc.is_zero())
        {
          TToken_string& row = gm.add_colored_row(sheet, 'R');
          row.add(TR("Ritenute sociali"), 4); 
          gm.add_importo(row, soc, FALSE);
          row.add(sum.get(PART_TIPOMOV), 11);
          tot_lit += soc;
        }
      }
      
      if (lastrow > 0)
      {        
        TToken_string& sp = gm.add_colored_row(sheet, 'T');
        sp.add(TR("Saldo partita"), 4);
        if (prima_valuta.in_valuta())
          sp << ' ' << prima_valuta.codice();
#ifdef __EXTRA__
        prima_valuta.set(gm, P_VALUTA, P_DATACAMBIO, P_CAMBIO);
#endif         
        gm.add_importo(sp, tot_lit);
        gm.add_importo(sp, tot_val, prima_valuta.codice());
      }
    
      if (should_delete_game)
        delete game;
    }
    
    if (prima_valuta.in_valuta() != gm._valfirst)
    { 
      sheet.swap_columns(106, 107); // Scambia le colonne dell'importo in lire e in valuta
      gm._valfirst = !gm._valfirst;
    }
    
    TString80 header;
    header = TR("Importo in "); header << TCurrency::get_firm_val();
    sheet.set_column_header(106, header);

    header = TR("Importo in "); 
    if (prima_valuta.in_valuta())
      header << prima_valuta.codice();
    else
      header << TR("valuta");
    sheet.set_column_header(107, header);
    
    sheet.force_update();   
    if (sheet.items() > 0)
      sheet.select(0, TRUE);
    else  
      sheet.force_update(); 
  }   
  
  if (k == K_INS)
  {
    gm.send_key(K_CTRL + 'N', 0, &partite);  // Simula la pressione del tasto nuovo
    return FALSE;                            // Rifiuta l'aggiunta di una riga
  }
  
  return TRUE;
}

bool TGame_mask::scadenze_notify(TSheet_field& scadenze, int r, KEY k)
{
  if (k == K_INS)
  {
    TGame_mask& gm = (TGame_mask&)scadenze.mask();
    gm.send_key(K_CTRL + 'N', 0, &scadenze); // Simula la pressione del tasto nuovo
    return FALSE;                            // Rifiuta l'aggiunta di una riga
  }
  return TRUE;
}


// Complesso algoritmo per calcolare la sezione di una nuova riga partita
char TGame_mask::calcola_sezione(tipo_movimento tm) const
{ 
  char sezione = ' ';
  const char tipoc = conto().tipo();

#ifndef __EXTRA__ 
  const TCausale& causale = app().causale();
  tm = (tipo_movimento)causale.tipomov();
  sezione = causale.sezione(1);                     // Usa la sezione della causale
#endif
  
  if (sezione <= ' ')                               // Se non c'e' la sezione bell'e' ch'e' pronta
  {
    if (tm == tm_fattura || tm == tm_insoluto)      // calcola in base al tipo movimento e
      sezione = (tipoc == 'C') ? 'D' : 'A';         // al tipo cliente/fornitore
    else  
      sezione = (tipoc == 'C') ? 'A' : 'D';
  }

#ifndef __EXTRA__ 
  // Gestione compensazioni
  if (tipoc > ' ')                                  // Se il tipo e' C o F
  {
    TBill bill; causale.bill(1, bill);              // Legge primo conto causale
    const char tc = bill.tipo();
    if (tc > ' ' && tc != tipoc)
      sezione = (sezione == 'D') ? 'A' : 'D';       // scambia segno
  }
#endif

  return sezione;
}

int TGame_mask::nuova_riga(TPartita& partita, tipo_movimento tm) const
{ 
  const int una_riga = partita.last();              // Memorizza una riga valida

  TRiga_partite& part = partita.new_row();          // Creazione nuova riga vuota
  const int nriga = part.get_int(PART_NRIGA);       // Nuova riga
  
  // Copia dati movimento corrente
  part.put(PART_NREG,   _numreg);                   // Numero operazione
  part.put(PART_NUMRIG, _numrig);                   // Riga su cui ho cliccato
  
  // Forza il gruppo/conto cliente corretto 
  part.put(PART_GRUPPOCL, conto().gruppo());
  part.put(PART_CONTOCL,  conto().conto());
  
  part.put(PART_TIPOMOV, (int)tm);       
  
  if (una_riga > 0)
  {
    const char* valuta = partita.riga(una_riga).get(PART_CODVAL);
    part.put(PART_CODVAL, valuta);
  }

#ifdef __EXTRA__    
  const TDate oggi(TODAY);
  const char* s = oggi.string();
  part.put(PART_DATADOC, s);
  part.put(PART_DATAREG, s);
#else
  // Setta il cambio corrente
  const TValuta valuta(*this, P_VALUTA, P_DATACAMBIO, P_CAMBIO, P_CONTROEURO);
  valuta.put(part);

  TMask& cm = app().curr_mask();
  const TCausale& causale = app().causale();
  part.put(PART_NUMDOC,  cm.get(F_NUMDOC));
  part.put(PART_DATADOC, cm.get(F_DATADOC));
  part.put(PART_DATAREG, cm.get(F_DATAREG));
  part.put(PART_DESCR,   cm.get(F_DESCR));
  part.put(PART_DATAPAG, cm.get(F_DATAREG));
  part.put(PART_TIPOPAG, 1);
  
  const TRiga_partite* prima = (tm != tm_fattura) ? cerca_prima_riga() : NULL;
  if (prima != NULL && prima != &part)
  {
    part.put(PART_DESCR,   prima->get(PART_DESCR));
    part.put(PART_DATAPAG, prima->get(PART_DATAPAG));
    part.put(PART_TIPOPAG, prima->get(PART_TIPOPAG));
  }
  
  // Copia dati causale corrente
  part.put(PART_CODCAUS, causale.codice());
  if (causale.iva() != nessuna_iva)
  {
    part.put(PART_REG,     cm.get(F_CODREG));
    part.put(PART_PROTIVA, cm.get(F_PROTIVA));
  }   
#endif    

  const char sezione = calcola_sezione(tm);
  
  // Memorizza solo la sezione (importi nulli)  
  part.put(PART_SEZ,       sezione);
  part.put(PART_SEZABB,    sezione); 
  part.put(PART_SEZDIFCAM, sezione);
  
  return nriga;
}

int TGame_mask::nuovo_pagamento(TPartita& partita, int nriga, int rata, tipo_movimento tm) const
{              
#ifdef __EXTRA__                
  const int nrigp = nuova_riga(partita, tm);
#else
  int nrigp = partita.mov2rig(_numreg, _numrig);       // Cerca riga partita relativa alla riga rmov
  const bool nuovo = nrigp <= 0;
  if (nuovo)                                      // Devo creare una nuova riga di partita
    nrigp = nuova_riga(partita, tm);
#endif
  TRectype& pagamento = partita.pagamento(nriga, rata, nrigp);      // Crea nuovo pagamento
  
  // Calcola riga causale per la contropartita in base al tipo pagamento
  int caus = 2;          
  
  if (nriga != TPartita::UNASSIGNED)
  {       
    const TRiga_partite& fatt = partita.riga(nriga);
    const TRiga_scadenze& scad = fatt.rata(rata);
    const int tp = scad.get_int(SCAD_TIPOPAG);
    caus = partita.tipopag2causale(tp);
    
    TRiga_partite& somma = partita.riga(nrigp);
    somma.put(PART_TIPOPAG, tp);
    somma.put(PART_CODVAL, fatt.get(PART_CODVAL));
    
    pagamento.put(PAGSCA_CODABIPR, scad.get(SCAD_CODABIPR));
    pagamento.put(PAGSCA_CODCABPR, scad.get(SCAD_CODCABPR));
    pagamento.put(PAGSCA_CODABI,   scad.get(SCAD_CODABI));
    pagamento.put(PAGSCA_CODCAB,   scad.get(SCAD_CODCAB));
    pagamento.put(PAGSCA_CODAG,    scad.get(SCAD_CODAG));
  }

#ifndef __EXTRA__  
  if (pagamento.get_int(PAGSCA_GRUPPOC) == 0)
  {
    const TCausale& causale = app().causale();
    TBill contro; causale.bill(caus, contro);    // Legge conto contropartita
    if (caus != 2 && contro.empty())             // Se non specificato ...
      causale.bill(caus = 2, contro);            // ... prende il primo
		if (contro.sottoconto() == 0L)
		{
			const int nrow = app().type2pos('I');
			
			if (nrow >= 0)
			{
				TSheet_field & cgs = app().cgs();
				contro.get(cgs.row(nrow), 3);
			}
		}
    contro.put(pagamento, TRUE);                 // Scrive conto contropartita
  }
#endif  
  
  return nrigp;
}

bool TGame_mask::edit_scadenza_handler(TMask_field& f, KEY k)
{             
  if (k == K_SPACE)
  {               
    TMask& m = f.mask();
    const int nriga = m.get_int(101);  // Numero riga fattura

    if (nriga == 0)
      return FALSE;                    // Ho cliccato su di un saldo (per sbaglio!)
    
    TGame_mask& gm = (TGame_mask&)(m.get_sheet()->mask());
    const TBill& bill = gm.conto();             // Clifo 
    
    TSheet_field& sp = gm.partite();
    TToken_string& spr = sp.row(gm._riga_partite);
    const int anno = spr.get_int(0);               // Anno
    const TString numero = spr.get();              // Numero
    
    TPartite_array& giochi = app().partite();
    TPartita* was = giochi.exist(bill, anno, numero);      // Controlla esistenza nell'array
    TPartita& game = was ? *was : giochi.partita(bill, anno, numero);
    
    long nreg = m.get_long(108);       // Numero registrazione
    const int nrata = m.get_int(102);  // Rata selezionata (puo' essere 0)
    int nrigp = m.get_int(113);        // Pagamento selezionato (puo' essere 0)
    
    if (nrata != 0 && nrigp == 0)
    {          
      if (m.get_bool(114))
      {
        if (was == NULL) giochi.destroy(game);
        return f.error_box(FR("La rata %d e' bloccata."), nrata);
      }  

#ifndef __EXTRA__
      const TValuta parval(game.riga(nriga));
      const TValuta curval(gm, P_VALUTA, P_DATACAMBIO, P_CAMBIO, P_CONTROEURO);
      if (parval != curval)
      {
        TString16 c = parval.codice();
        if (c.empty()) c = TCurrency::get_firm_val();

        if (was == NULL) giochi.destroy(game);
        return f.error_box(FR("La fattura deve essere pagata in %s."), (const char*)c);
      }                   
#endif
      
      tipo_movimento tm;
#ifdef __EXTRA__
      TMask* nm = new TNew_mask(gm.conto().tipo(), FALSE, FALSE);
      nm->set(P_ANNO, game.anno());
      // nm->set(P_NUMERO, game.numero());  // Non deve proporre nulla: CM000206
      const KEY k = nm->run();
      tm = (tipo_movimento)nm->get_int(P_NUOVO);
      delete nm; nm = NULL;
      
      if (k != K_ENTER)
      {
        if (was == NULL) 
          giochi.destroy(game);
        return FALSE;
      }  
#else        
      tm = (tipo_movimento)app().causale().tipomov();
#endif    
      
      nrigp = gm.nuovo_pagamento(game, nriga, nrata, tm);
      nreg = gm._numreg;
    }
    
    bool cambiato  = FALSE;
    
    if (nrigp > 0)            // Si vuole editare un pagamento
    {                       
      if (nreg == gm._numreg)
      {
        cambiato = gm.edit_pagamento(game, nriga, nrata, nrigp);
        if (cambiato)
        {                              
#ifdef __EXTRA__
          game.rewrite();   // In extra-contabile salvo subito!
#endif            
        }  
      }
      else
      {
#ifdef __EXTRA__
        gm.prima_nota(nreg);
        if (m.is_running()) m.set_focus();
#else      
        if (was == NULL) giochi.destroy(game);
        return f.error_box(FR("Modificare il movimento %ld"), nreg);  
#endif        
      }  
    }
    else
    {                        // Si vogliono editare le rate
#ifdef __EXTRA__
      if (nreg > 0)
      {
        gm.prima_nota(nreg);
        if (m.is_running()) m.set_focus();
      }
      else  
        cambiato = gm.edit_fattura(game, nriga);
#else      
      if (nreg != gm._numreg || nrata == 0)
      {              
        if (nreg == 0)
          f.error_box(TR("Utilizzare la gestione extra-contabile"));
        else
          f.error_box(FR("Modificare il movimento %ld"), nreg);
        if (was == NULL) giochi.destroy(game);
        return FALSE;
      }  
#endif        
    }  
    
    if (cambiato)
    {                 
      if (m.is_running())               // Se la maschera e' aperta la chiudo
        m.stop_run(K_FORCE_CLOSE);
    
      TSheet_field& ss = gm.scadenze();
      const int rs = ss.selected();     // Memorizza la riga corrente
      const int rt = ss.items();        // Memorizza numero righe correnti
      
      gm.fill_partite();                // Aggiorna elenco partite
      
      const int rn = ss.items();        // Memorizza nuovo numero righe
      if (rs < rn)                      // Se esiste ancora ...
      {
        const bool scroll =  rn > rt;   // Scrolla solo se aggiungo righe
        ss.select(rs, scroll);          // ... riporta la selezione sulla riga corrente
      }
      gm._changed = TRUE;

      gm.set_focus();                   // A volte finisce sotto!
    }  
  }
  return TRUE;
}

bool TGame_mask::nuovo_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  { 
    TGame_mask& gm = (TGame_mask&)f.mask();
    int anno;
    TString numero;

#ifdef __EXTRA__
    const bool allow_fatt = TRUE;
#else
    const bool allow_fatt = FALSE;
#endif                        

    static TNew_mask* new_game = NULL;
    if (new_game == NULL)
      new_game = new TNew_mask(gm.conto().tipo(), allow_fatt, TRUE);     
    else
      new_game->init(gm.conto().tipo(), allow_fatt, TRUE);     
    
    tipo_movimento tm;

#ifndef __EXTRA__                     
    const TMask& cm = app().curr_mask();
    tm = (tipo_movimento)app().causale().tipomov();
    new_game->set(P_NUOVO, tm);
    if (tm == tm_nota_credito)
    {                    
      new_game->set(P_ANNO, cm.get(F_ANNORIF)); 
      new_game->set(P_NUMERO, cm.get(F_NUMRIF));
    }
#endif             

    k = new_game->run();
    tm = (tipo_movimento)new_game->get_int(P_NUOVO);
    anno   = new_game->get_int(P_ANNO);
    numero = new_game->get(P_NUMERO);
    
    // Distruzione maschera di richiesta
    // delete new_game; new_game = NULL;
    
    if (k == K_ENTER)
    {             
      // Creazione nuova partita         
      TPartita& game = app().partite().partita(gm.conto(), anno, numero);
      
      bool edit = FALSE;
      // N.B. Le fatture non possone essere editate in modo contabile
      if (tm != tm_fattura)     
      {                              
        const int nriga = TPartita::UNASSIGNED;
        const int nrata = TPartita::UNASSIGNED;
        const int nrigp = gm.nuovo_pagamento(game, nriga, nrata, tm);
        edit = gm.edit_pagamento(game, nriga, nrata, nrigp);
#ifdef __EXTRA__      
        if (edit) game.rewrite();
#endif        
      }   
#ifdef __EXTRA__      
      else
        edit = gm.edit_fattura(game, 0);
#endif    
      
      if (edit)         
      {
        gm.set(P_ANNO, anno);
        gm.set(P_NUMERO, numero);
        gm._changed = TRUE;
        
        // Aggiorna sheet partite: aggiunge la nuova partita e lo riordina
        gm.fill_partite();                    
      } // Sono state apportate modifiche 
    }
  }
  
  return TRUE;
}


///////////////////////////////////////////////////////////                          
// Metodi della maschera delle partite
///////////////////////////////////////////////////////////                          

void TGame_mask::add_importo(TToken_string& s, const TImporto& i, const char* valuta, int pos)
{
  if (i.is_zero())
    s.add("", pos);  
  else  
  {              
    TImporto n(i); n.normalize();
    const TCurrency curr(n.valore(), valuta);
    TString80 v = curr.string(FALSE); 
    if (!real::is_null(v))
    {
      v.replace('.', ',');  // Mette la virgola
      v << ' ' << n.sezione();
      s.add(v, pos);
    }
    else
      s.add("", pos);  
  }
} 

void TGame_mask::add_descrizione(TToken_string& s, const TRiga_partite& riga, int pos)
{
  const TString& desc = riga.get(PART_DESCR);
  if (desc.empty())
  {
    const TString& caus = riga.get(PART_CODCAUS);
    if (caus.not_empty())
      s.add(cache().get(LF_CAUSALI, caus, CAU_DESCR));
    else
      s.add("", pos);
  }
  else
    s.add(desc, pos);
}

TImporto TGame_mask::get_importo(TToken_string& s, int pos) const
{
  TFixed_string imp(s.get(pos)); imp.replace(',', '.');
  const real i(imp);
  const char sez = imp.right(1)[0];
  return TImporto(sez, i);
} 

int TGame_mask::update_partita(const TRectype& riga, 
                               const TImporto& saldo, const TImporto& doc, 
                               const TImporto& pag, const TImporto& imp, 
                               int prow)
{              
  TSheet_field& games = partite();
  TToken_string &r = games.row(prow);            // Stringa di lavoro per lo sheet
  if (!riga.empty())                             // Esiste veramente 
  {
    r.cut(0);
    r.add(riga.get(PART_ANNO)); r.right_just(4); // Mette gli spazi se ce n'e' bisogno
    r.add(riga.get(PART_NUMPART));
    r.add(riga.get(PART_DATADOC));
    r.add(riga.get(PART_NUMDOC));
    add_importo(r, saldo);
    add_importo(r, doc);
    add_importo(r, pag);
    add_importo(r, imp);
    r.add(riga.get(PART_DESCR)); 
  }
  else
  {
    r.add("", 4);
    r.add("", 5);
    r.add("", 6);
    r.add("", 7);
  }  
  
  if (prow >= 0)
  {            
    games.force_update(prow);
    update_saldo_clifo();
    games.force_update(games.items()-1);
    aggiorna_residuo();
  }
  else
    prow = partite().items()-1;
    
  if (prow == 0)
  {
    const char all = TPartita::allineamento_richiesto(conto().tipo());
    field(P_NUMERO).set_justify(all == 'R');
  }  
  
  return prow;    
}

int TGame_mask::update_partita(const TPartita& part, int prow)
{                                            
  int r = part.prima_fattura();
  if (r <= 0) r = part.first();

  TImporto saldo, doc, pag, imp;                       
  part.calcola_saldo(saldo, doc, pag, imp);
  prow = update_partita(part.riga(r), saldo, doc, pag, imp, prow);
  
  return prow;
}

int TGame_mask::update_partita(const TRectype& riga, int prow)
{                                            
  TImporto saldo, doc, pag, imp;
  
  TRectype rec = riga;
  TPartita::read_saldo(rec, saldo, doc, pag, imp);
  return update_partita(rec, saldo, doc, pag, imp, prow);
}

void TGame_mask::update_saldo_clifo()
{                    
  TString_array& s = partite().rows_array();
  
  TImporto sal, doc, pag, imp;
  int i;
  
  for (i = 0; i < s.items(); i++)
  {                  
    TToken_string& r = s.row(i);
    if (r.get_int(0) > 0)
    {
      sal += get_importo(r, 4);
      doc += get_importo(r, -1);
      pag += get_importo(r, -1);
      imp += get_importo(r, -1);
    }  
    else
      break;
  }
  
  TToken_string& r = s.row(s.add("", i));
  r.add(""); 
  r.add("");
  r.add(TDate(TODAY).string());
  r.add("");
  add_importo(r, sal);
  add_importo(r, doc);
  add_importo(r, pag);
  add_importo(r, imp);
  r.add(TR("Saldo ")); 
  append_conto(r);
  
  COLOR back, fore;
  app().type2colors('T', back, fore);
  partite().set_back_and_fore_color(back, fore, i);
  if (i > 0)  // Se ho aggiunto una riga devo decolorare la precedente
    partite().set_back_and_fore_color(NORMAL_BACK_COLOR, NORMAL_COLOR, i-1);
}

long TGame_mask::number_distance(const char* k, const char* n) const
{
  TString16 key(k); key.upper(); key.trim(); 
  const int kl = key.len();
  TString16 num(n); num.upper(); num.trim();
  const int nl = num.len();
  long dist = 0;
  for (int i = kl-1; i >= 0; i--)
  {
    const char kc = i < kl ? key[i] : 0;
    const char nc = i < nl ? num[i] : 0;
    const long d = abs(kc - nc) * (kl - i) * 32;
    dist += d;
  }
  return dist;
}

bool TGame_mask::same_number(const char* key, const char* num) const
{
  TString16 k(key); k.upper(); k.trim();
  TString16 n(num); n.upper(); n.trim();
  return k == n;
}     

bool TGame_mask::partita_aperta(const TRectype& part) const
{
  if (part.get_bool(PART_CHIUSA))
    return FALSE;
 
  TRectype sld(part);                     
  TImporto saldo, doc, pag, imp;
  const int err = TPartita::read_saldo(sld, saldo, doc, pag, imp);
  if (err == NOERR && saldo.is_zero())
    return FALSE;
  
  return TRUE;
}

static int sort_games_num(const TObject** o1, const TObject** o2)
{
  TToken_string& r1 = *(TToken_string*)*o1;
  TToken_string& r2 = *(TToken_string*)*o2;
  int diff = r1.get_int(0) - r2.get_int(0); // Anno
  if (diff == 0)
  {
    diff = r1.get_long(1) - r2.get_long(1); // Numero Partita
    if (diff == 0)
      diff = r1.get_long(3) - r2.get_long(3); // Numero Documento
  }
  return diff;
}

static int sort_games_date(const TObject** o1, const TObject** o2)
{
  TToken_string& r1 = *(TToken_string*)*o1;
  TToken_string& r2 = *(TToken_string*)*o2;
  int diff = r1.get_int(0) - r2.get_int(0); // Anno
  if (diff == 0)
  {
    const TDate d1 = r1.get(2);
    const TDate d2 = r2.get(2);
    diff = d1 - d2; // Data documento
    if (diff == 0)
      diff = r1.get_long(3) - r2.get_long(3); // Numero Documento
  }
  return diff;
}

void TGame_mask::sort_partite()
{
  TString_array& a = partite().rows_array();

  // Elimina riga saldo se presente
  int last = a.last();
  if (last > 0 && a.row(last).get_int(0) == 0)  // Se l'anno dell'ultima riga e' nullo ...
    a.destroy(last--);                          // ... elimina riga saldo

  // Ordina lista se necessario (ci sono almeno due partite)
  if (last > 0)
  {
    switch (get(P_SORT)[0])           
    {
    case 'N': a.TArray::sort(sort_games_num); break;  // Ordine numerico
    case 'D': a.TArray::sort(sort_games_date); break; // Ordine per data
    default : a.sort(); break;                        // Ordine alfabetico
    }
    update_saldo_clifo();  // Ricrea riga saldo 
  }
  
  partite().force_update(); // Aggiorna lo spreadsheet 
}

void TGame_mask::fill_partite()
{ 
  TWait_cursor hourglass;
                                                
  const int annorif = get_int(P_ANNO);           // Anno corrente    
  const TString16 numrif = get(P_NUMERO);        // Partita corrente
  const bool all = get(P_SHOWALL).not_empty();   // Visualizza anche partite chiuse
                     
  partite().destroy();                            
  TString_array& a = partite().rows_array();

  TPartite_array& giochi = app().partite();
  for (TPartita* gioco = giochi.first(); gioco != NULL; gioco = giochi.next())
  {     
    // Visualizza solo le partite con almeno una riga!  Non posso scaricarle a priori in quanto
    // potrebbero essere state cancellate proprio ora e quindi devo tenerle cosi' per aggiornare
    // correttamente gli archivi.
    if (gioco->ok())     
    {        
      const TBill& k = gioco->conto();       
      bool u = (k.tipo() > ' ' && k.sottoconto() == conto().sottoconto()) || k == conto();

      if (u && !all && gioco->chiusa() && gioco->mov2rig(_numreg, _numrig) <= 0)                     
        u = false;
      
      if (u)
        update_partita(*gioco, -1);             
    }  
    else
    {                               // Se la partita e' vuota ...
      if (!gioco->is_on_file())     // ... e non esiste su file
        giochi.destroy(*gioco);     // posso eliminarla tranquillamente
    }
  }
  
  TLocalisamfile partita(LF_PARTITE);
  TRectype& curpar = partita.curr();
  curpar.zero();
  if (conto().tipo() > ' ')                   // Ignora gruppo e conto dei clifo
  {
    curpar.put(PART_TIPOCF, conto().tipo());             
    curpar.put(PART_SOTTOCONTO, conto().codclifo()); 
  }
  else 
    conto().put(curpar);                      // Scrive completamente i conti normali
  
  const TRectype filter(curpar);              // Record campione

  for (int err = partita.read(_isgreat); 
       err == NOERR && curpar == filter; 
       err = partita.read(_isgreat))
  {
    if (!giochi.exist(curpar))
    {
      if (all || partita_aperta(curpar))
        update_partita(curpar, -1);
    }
    // Forza lettura partita successiva nella prossima read
    curpar.put(PART_NRIGA, (int)TPartita::UNASSIGNED); 
  }

  sort_partite();
  aggiorna_residuo();
  
  if (!a.empty())
  {
    _riga_partite = -1;
    
    int found = -1;
    for (int r = a.last(); r >= 0; r--)
    {
      TToken_string& row = a.row(r);
      const int anno = row.get_int(0);
      const TString16 numero = row.get(1);
      
      if (found < 0 && annorif == anno && same_number(numrif, numero))
        found = r;

      TPartita* gioco = app().partite().exist(conto(), anno, numero);
      if (gioco != NULL && gioco->mov2rig(_numreg, _numrig) > 0)
      {
        COLOR back, fore;
        app().type2colors('X', back, fore);
        partite().set_back_and_fore_color(back, fore, r);
      }
    }
    if (found < 0) found = 0;
    partite().select(found, TRUE);
    _ignore_next_select = !is_running();
  }  
  else
  {
    scadenze().destroy();
  }  
}

bool TGame_mask::edit_pagamento(TPartita& p, int nriga, int nrata, int nrigp) const
{ 
  TRectype oldpag = p.pagamento(nriga, nrata, nrigp);
  TRiga_partite& somma  = p.riga(nrigp);

  const bool nuovo = oldpag.get_char(PAGSCA_ACCSAL) != 'S' && 
                     oldpag.get_real(PAGSCA_IMPORTO).is_zero() &&
                     oldpag.get_real(PAGSCA_RITENUTE).is_zero() &&
                     oldpag.get_real(PAGSCA_RITSOC).is_zero();

  // We must create masks on the heap
  static TPay_mask* pm = NULL;
  if (pm == NULL)
    pm = new TPay_mask(this, nuovo ? MODE_INS : MODE_MOD);              
  else
    pm->init(this, nuovo ? MODE_INS : MODE_MOD);               
  TPay_mask& m = *pm;
  
  if (nriga == TPartita::UNASSIGNED)
  {              
    TRiga_scadenze& scaden = somma.new_row();        // Crea una rata falsa
    scaden.put(SCAD_DATASCAD, somma.get(PART_DATADOC));
    m.set_pag(oldpag, scaden, _residuo);
    somma.destroy_rows();                            // Distrugge la rata falsa
  }  
  else
  {
    const TRiga_scadenze& scaden = p.rata(nriga, nrata);
    m.set_pag(oldpag, scaden, _residuo);
  }
  if (nuovo)   // Inizializza automaticamente l'importo
  {
    TMask_field& imp = m.field(S_IMPORTO);
    imp.set_dirty();
    imp.on_key(K_F8);
  }       

  KEY key = m.run();

  if (key == K_DEL)
  {
    const bool truly = yesno_box(TR("Confermare l'eliminazione"));
    if (!truly) key = K_ESC;
  }
  
  if (key == K_ESC && nuovo)
    key = K_DEL;
  
  if (key != K_ESC)
  {
    TRectype newpag(oldpag);

    if (key == K_DEL)
    {
      newpag.zero(PAGSCA_ACCSAL);                  // Non puo' essere un saldo
      newpag.zero(PAGSCA_IMPORTO);                 // Azzera importo ...
      newpag.zero(PAGSCA_IMPORTOVAL);              // .. anche in valuta
      newpag.zero(PAGSCA_RITENUTE);                // Azzera ritenute
      newpag.zero(PAGSCA_RITSOC);
    }
    else
    {
      m.get_pag(newpag, somma);   
    }
    
    const TValuta val(somma);                              
#ifdef __EXTRA__
    p.modifica_pagamento(newpag, val, TRUE);
#else  
    if (somma.is_nota_credito())      
      p.modifica_pagamento(newpag, val, TRUE);
    else
      app().notify_edit_pagamento(p, newpag, val);
#endif          
  }       
  
  // delete pm; pm = NULL;  // commentata in quanto statica

#ifdef __EXTRA__
  xvtil_statbar_set(TR("Estratto conto"), TRUE);
#endif  
  
  return key != K_ESC;
}

#ifndef __EXTRA__

///////////////////////////////////////////////////////////
// Edit delle partite
///////////////////////////////////////////////////////////

bool TPrimanota_application::edit_partite(const TMask& m, int riga)
{                           
  const char tipo = m.get(CG_TIPO)[0];
  const char rt = m.get(CG_ROWTYPE)[0];
  if (rt == 'T' && tipo <= ' ')           // Nelle note di credito DEVE essere un clifo
    return FALSE;

  const int gruppo = m.get_int(CG_GRUPPO);
  const int conto = m.get_int(CG_CONTO);
  const long sottoconto = m.get_long(CG_SOTTOCONTO);
  TBill b(gruppo, conto, sottoconto, tipo);  // Legge il conto della riga selezionata
  
  // Esci se il conto della riga cliente non e' valido
  if (!b.ok() || !b.find()) 
    return m.field(CG_SOTTOCONTO).error_box(TR("Conto assente"));
  
  // Aggiorna conto sulla riga contabile  
  b.add_to(cgs().row(riga), 3, 0x0);
    
  TMovimentoPN* pn = (TMovimentoPN*)get_relation();
  curr_mask().autosave(*pn);   // Aggiorna i dati della testata sulle partite
  long numreg = pn->curr().get_long(MOV_NUMREG);
  if (curr_mask().insert_mode())
    numreg = NUMREG_PROVVISORIO;
  
  partite().update_reg(pn->curr()); 
 
  TConfig cfg(CONFIG_GUI, "sc");

  bool changed = false;
  if (cfg.get_bool("EasyMode"))
  {
    // Nuova maschera di gestione partite
    static TEasySolder_mask* mask = NULL;
    if (mask == NULL) 
      mask = new TEasySolder_mask(b, numreg, riga+1); 
    else                                        
      mask->init(b, numreg, riga+1); 
    mask->run();        
    changed = mask->changed();
  }
  else
  {
    // Maschera tradizionale di gestione partite
    static TGame_mask* mask = NULL;
    if (mask == NULL) 
      mask = new TGame_mask(b, numreg, riga+1); 
    else                                        
      mask->init(b, numreg, riga+1); 
    mask->run();        
    changed = mask->changed();
  }
  
  if (changed)
  {
    cgs().force_update();      // Aggiornamento righe contabili
    calcola_saldo();
    _sal_dirty = true;
  }
  return changed;
}

bool TPrimanota_application::crea_partita(const TBill& bill, int anno, const TString& numero, int numrig, const TImporto& importo)
{
  TPartita& partita = app().partite().partita(bill, anno, numero);

  TRiga_partite& part = partita.new_row();          // Creazione nuova riga vuota
  const int nriga = part.get_int(PART_NRIGA);       // Nuova riga
  
  // Copia dati movimento corrente
  part.put(PART_NREG,   NUMREG_PROVVISORIO);        // Numero operazione
  part.put(PART_NUMRIG, numrig);                    // Riga su cui ho cliccato
  
  // Forza il gruppo/conto cliente corretto (lo fa gia' anche la new_row)
  part.put(PART_GRUPPOCL, bill.gruppo());
  part.put(PART_CONTOCL,  bill.conto());
  
  const TCausale& causale = app().causale();
  const tipo_movimento tm = (tipo_movimento)causale.tipomov();
  part.put(PART_TIPOMOV, int(tm));       
  
  TMask& cm = app().curr_mask();

  // Setta il cambio corrente
  const TValuta valuta(cm, SK_VALUTA, SK_DATACAMBIO, SK_CAMBIO);
  valuta.put(part);

  part.put(PART_NUMDOC,  cm.get(F_NUMDOC));
  part.put(PART_DATADOC, cm.get(F_DATADOC));
  part.put(PART_DATAREG, cm.get(F_DATAREG));
  part.put(PART_DESCR,   cm.get(F_DESCR));
  part.put(PART_DATAPAG, cm.get(F_DATAREG));
  part.put(PART_TIPOPAG, 1);   // Rimessa diretta
  
  // Copia dati causale corrente
  part.put(PART_CODCAUS, causale.codice());
  if (causale.iva() != nessuna_iva)
  {
    part.put(PART_REG,     cm.get(F_CODREG));
    part.put(PART_PROTIVA, cm.get(F_PROTIVA));
  }   

  const char sezione = importo.sezione();
  // Memorizza solo la sezione (importi nulli)  
  part.put(PART_SEZ,       sezione);
  part.put(PART_SEZABB,    sezione); 
  part.put(PART_SEZDIFCAM, sezione);
  
  if (tm != tm_fattura)
  {
    // Crea nuovo pagamento
    TRectype pagamento = partita.pagamento(TPartita::UNASSIGNED, TPartita::UNASSIGNED, nriga);
    const real implit = importo.valore(); 
    pagamento.put(PAGSCA_IMPORTO, implit);
    if (valuta.in_valuta())
    {                                                    
      const real impval = valuta.lit2val(implit);
      pagamento.put(PAGSCA_IMPORTOVAL, impval);
    }
    partita.modifica_pagamento(pagamento, valuta, TRUE);
  }

  return TRUE;
}

#endif