#include <automask.h>
#include <colmask.h>
#include <defmask.h>
#include <execp.h>
#include <progind.h>
#include <relapp.h>
#include <urldefid.h>

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

#include "lvcondv.h"
#include "lvrcondv.h"

#include "lvlib.h"
#include "lvlib2.h"
#include "../mg/clifogiac.h"
#include "../ve/rcondv.h"

#include "lv0400.h"

//LV_NEW_CONTRACT: metodo generale utilizzato sia nella maschera che nell'applicazione
//che restituisce il primo codcont libero
long lv_new_contract(long cliente, int indsped)
{
  //leggo dalla configurazione se la numerazione dei contratti 
  //è sequenziale per ditta o per cliente  
  long codcont = 0;
  
  if (ini_get_bool(CONFIG_DITTA, "lv", "UniCont"))  //se la numerazione è per ditta, cerco in tutto LVCONDV il codcont più grande
  {
    TISAM_recordset recset ("USE LVCONDV");
    for (bool ok=recset.move_first(); ok; ok = recset.move_next())
    {
      const long codice=recset.get("CODCONT").as_int();
      if (codice>codcont)
        codcont=codice;
    }
  }
  else  //altrimenti cerco il codcont più grande di un determinato cliente
  {
   TString query; 
   query<<"USE LVCONDV\n"
        <<"FROM CODCF=#CLIENTE\n"
        <<"TO CODCF=#CLIENTE\n";
   TISAM_recordset recset (query);
   recset.set_var("#CLIENTE",cliente);
   if (recset.move_last())
     codcont=recset.get("CODCONT").as_int();     
  }
  codcont++;
  return codcont;
}

class TColor_rule_contract : public TExpression
{
  COLOR _back, _fore, _def_back, _def_fore;
  TString _desc, _key;

public:
  const TString& description() const { return _desc; }
  const TString& key() const { return _key; }
  void default_colors(COLOR& back, COLOR& fore) const { back = _def_back; fore = _def_fore; }
  void colors(COLOR& back, COLOR& fore) const { back = _back; fore = _fore; }
  void set_colors(COLOR back, COLOR fore) { _back = back; _fore = fore; }
  
  TColor_rule_contract(const char* desc, const char* expr, TTypeexp type, COLOR back, COLOR fore);
};

TColor_rule_contract::TColor_rule_contract(const char* desc, const char* expr, TTypeexp type, COLOR back, COLOR fore)
										: TExpression(expr, type), _back(back), _fore(fore), _def_back(back), _def_fore(fore)
{
  _desc = dictionary_translate(desc);
  _key = desc; _key.trim(); _key.strip_double_spaces();
  _key.replace(' ', '_');
}


                                     //////////////////////////////
                                     ////    TCONTRATTI_MSK    ////
                                     ////////////////////////////// 

//classe TContratti_msk
class TContratti_msk: public TAutomask
{
  long _post_contr;
  TString80 _artrig;
  int       _riga;
	TArray		_color_rules;

protected:
  void azzera_conguaglio();
  bool on_art_select(TField_event e);  
  virtual void on_idle();
  virtual bool on_field_event(TOperable_field& o,TField_event e,long jolly);

	TArray& color_rules() { return _color_rules; }

public:
  int get_riga();
  bool set_riga(const int val);

	void sel_color();
	void highlight();
	void highlight_row(int row = -1, COLOR back = COLOR_INVALID, COLOR fore = COLOR_INVALID, bool dirty = true, bool update = true);
  TContratti_msk();
};

//AZZERA_CONGUAGLIO: metodo che azzera tutti conguagli di un contratto dopo aver dato la conferma
void TContratti_msk::azzera_conguaglio()
{
  TLaundry_contract cont(get_long(F_CODCF), get_long(F_CODCONT));

  TSheet_field& sheet = sfield(F_RIGHE);

  FOR_EACH_SHEET_ROW(sheet, r, row)
    row->add(0L, sheet.cid2index(S_CONG));

  sheet.force_update();
}

void TContratti_msk::highlight_row(int row, COLOR back, COLOR fore, bool dirty, bool update)
{			 
	TSheet_field& sf = sfield(F_RIGHE);
	
	if (row < 0)
		row = sf.selected();

  FOR_EACH_ARRAY_ITEM_BACK(color_rules(), rule, o)
	{
		TColor_rule& expr = *(TColor_rule*)o;
    bool on = false;

		// SET VARS

		const int vars = expr.numvar();
    TString name;
		for (int i = 0; i < vars; i++)
		{
			name = expr.varname(i);
			if (name.starts_with("DIRTY"))
				expr.setvar(i, dirty ? UNO : ZERO);
			else
				if (name.starts_with("TODAY"))
					expr.setvar(i, TDate(TODAY).string());
				else
					if (name.starts_with("#"))
					{
						const short id = atoi(name.mid(1));
						if (id > 0)
						{
							TToken_string& sheet_row = sf.row(row);
							expr.setvar(i, sheet_row.get(sf.cid2index(id)));
						}
						else
							expr.setvar(i, get(-id));
					}
					else
						if (name.starts_with("48.") || name.starts_with("ANAMAG."))
						{
							TToken_string & row = sf.row(sf.selected());
							const TRectype & art = cache().get(LF_ANAMAG, row.get(sf.cid2index(S_CODART)));
							const TString& fldname = name.after('.');

							expr.setvar(i, art.get(fldname));
						}
		}
    if (expr.as_bool())
    {
      expr.colors(back, fore);
      break;
    }
	}

  sf.set_back_and_fore_color(back, fore, row);
  if (update)
		sf.force_update(row);
}

void TContratti_msk::highlight()
{			 
	TSheet_field& sf = sfield(F_RIGHE);
  FOR_EACH_SHEET_ROW(sf, i, r)
    highlight_row(i, COLOR_INVALID, COLOR_INVALID, false, false);
	sf.force_update();
}
void TContratti_msk::sel_color()
{
	TFilename mask(source_file());

	TSelect_color_mask sel(mask.name_only(), "0");

	FOR_EACH_ARRAY_ITEM(color_rules(), i, o)
	{
		const TColor_rule& col = *(const TColor_rule*)o;
    COLOR a, b; col.colors(a, b);
    COLOR c, d; col.default_colors(c, d);
  	sel.add_color(col.key(), col.description(), a, b, c, d);
	}

	if (sel.run() != K_ESC)
	{
  	FOR_EACH_ARRAY_ITEM(color_rules(), i, o)
	  {
		  TColor_rule& col = *(TColor_rule*)o;
      COLOR back, fore; sel.get_color(col.key(), back, fore);
		  col.set_colors(back, fore);
    }
		highlight();
	}
}

//ON_ART_SELECT: metodo che riempie i campi delle dotazioni e del consegnato sullo sheet e sulla maschera
//e riporta i dati dello sheet nel dettaglio sulla maschera (sotto lo sheet)
bool TContratti_msk::on_art_select(TField_event e) 
{
  //dallo sheet identifico la riga selezionata e estraggo i dati di interesse
  TSheet_field& ss = sfield(F_RIGHE);  
  TToken_string& row  = ss.row(ss.selected());
  const TString80 codart(row.get(ss.cid2index(S_CODART)));
  const long codcf = get_long(F_CODCF);
  const int indsped = get_int(F_INDSPED);
  
  //instanzio un TArticolo_lavanderie per poter recuperare i dati di interesse
  TArticolo_lavanderie& artrec = cached_article_laundry(codart, 'C', codcf, indsped);
	
  //setto datasc a oggi e fisso l'anno esercizio
  TEsercizi_contabili& esc = esercizi();
  const int last_esc = esc.last();

  //estraggo il record corrispondente su LF_CLIFOGIAC	
  const TRecmag_lavanderie& reclav = artrec.find_rec(last_esc);
  //recupero la maschera di riga
  TMask& rowmask = ss.sheet_mask(); 

  if (!ini_get_bool(CONFIG_DITTA, "lv", "Qtamodi"))
  {
    field(F_DOTTMP).disable();  
    rowmask.field(S_DOTIN).disable();
    rowmask.field(S_DOTOD).disable();
    rowmask.field(S_DOTTMP).disable();
  }
	
  if (rowmask.get(S_UM).blank() && artrec.um().rows() > 0)
  {
    const TString& um = artrec.um()[1].get(UMART_UM);
    rowmask.set(S_UM, um);
  }

  //se esiste il record su LF_CLIFOGIAC, recupero l'unità di misura dalla riga dello sheet selezionata
  //e setto i campi delle dotazioni e dei consegnati ai valori corretti riportati alla giusta unità di misura
  real dotin     = ZERO;
  real dotod     = ZERO;
  real dottmp    = ZERO;
  real consyear  = ZERO;
  real consmonth = ZERO;

  if (!reclav.empty())
	{
    if (rowmask.get(S_DOTIN).blank())
    {
      //calcolo dotazione iniziale, scritta sia sulla maschera che sullo sheet
	    dotin = reclav.get_real(CLIFOGIAC_DOTIN);		  
      //calcolo dotazione odierna, scritta sia sulla maschera che sullo sheet
	    dotod = reclav.get_real(CLIFOGIAC_DOTOD);		  
      //calcolo dotazione temporanea, scritta sia sulla maschera che sullo sheet
	    dottmp = reclav.get_real(CLIFOGIAC_DOTTM);
    }
    //calcolo consegnato anno, scritto sia sulla maschera che sullo sheet
	  consyear = reclav.get_real("CONSANNO");	  
    //calcolo consegnato mese, scritto sia sulla maschera che sullo sheet
		consmonth = reclav.get_real("CONSMESE");		
	}
  
  if (rowmask.get(S_DOTIN).blank())
  {
    rowmask.set(S_DOTIN, dotin);
    rowmask.set(S_DOTOD, dotod);
    rowmask.set(S_DOTTMP, dottmp);
  }
  rowmask.set(S_CONSANNO, consyear);
  rowmask.set(S_CONSMESE, consmonth);

  //instanzio una cache sull'anagrafica di magazzino
  //per leggere il valore di PPCONF corretto e sempre aggiornato
  const TRectype& anamag = cache().get(LF_ANAMAG, codart);
  int ppconf = anamag.get_int(ANAMAG_PPCONF);
  real cosrotti = anamag.get_real(ANAMAG_ULTCOS1);
  rowmask.set(S_PPCONF, ppconf);
  row.add(ppconf, ss.cid2index(S_PPCONF));
  if (rowmask.get(S_PREZDAN).empty())
  {
    rowmask.set(S_PREZDAN, cosrotti);
    row.add(cosrotti.string(), ss.cid2index(S_PREZDAN));
  }

  //ciclo i dati di interesse della riga selezionata nel dettaglio
  //sulla maschera principale
	if (e != fe_init)
		for (short id = F_CODART; id <= F_CODART + 36; id++)
		{
			const int pos = id2pos(id);
			if (pos > 0)
			{
				TMask_field& f = fld(pos);
				const TString& oldval = f.get();
				const char* newval = row.get(ss.cid2index(id - 400));
				if (oldval != newval)
				{
					f.set(newval);
					if (f.is_kind_of(CLASS_LIST_FIELD))
						f.on_hit();
				}
			}
		}

  if (ppconf <= 0)
  {
    set(F_CALCCONS, "0");    
    disable(F_CALCCONS);    
    rowmask.set(S_CALCCONS, "0");
    rowmask.field(S_CALCCONS).disable();
    rowmask.field(S_CONG).disable();
  }
  else
  {
    enable(F_CALCCONS);    
    rowmask.field(S_CALCCONS).enable();
    rowmask.field(S_CONG).enable();
    TToken_string row = ss.row(ss.selected());
  }
	return true;
}


//ON_IDLE: ridefinizione del metodo on_idle() delle TAutomask per settare il focus
//nel posto desiderato
void TContratti_msk::on_idle()
{
  TAutomask::on_idle();

  TSheet_field& s = sfield(F_RIGHE);
  //se riconosco in quale riga devo andare, setto il focus su quella riga
  if (_riga >= 0)
  {
    field(F_RIGHE).set_focus();
    s.set_focus_cell_id(_riga, S_CODART);
    _riga = -1;
  }
  else
    if (_post_contr > 0)
    {
      set(F_CODCONT, _post_contr);
      _post_contr = 0;
      efield(F_CODCONT).set_focus();
    }
}

static const TString& doc_rif(const TRecordset& rdoc)
{
  TString& tmp = get_tmp_string();
  tmp << rdoc.get(RDOC_CODNUM) << ' ' << rdoc.get(RDOC_ANNO) << '/' << rdoc.get(RDOC_NDOC);
  return tmp;
}

//ON_FIELD_EVENT: definizione del metodo che setta i comportamenti dei vari campi della mashera
bool TContratti_msk::on_field_event(TOperable_field& o,TField_event e,long jolly)
{
  switch(o.dlg())
  {
  case F_CODCF:
  case F_RICALT:
    //se sono in query_mode e se il campo risulta pieno e modificato,
    //e il codcont vuoto, allora riempio in automatico l'indirizzo di spedizione
    //e propongo il contratto valido nella giornata di oggi
    if (query_mode())  
    {
      if (e == fe_modify && !o.empty() && efield(F_CODCONT).empty())
      {
        const long codcf = atol(o.get());
        const int indsped = get_int(F_INDSPED);
        const TDate oggi(TODAY);
        _post_contr = lv_find_contract(codcf, indsped, oggi);
        field(F_RAGSOC).set_focus();
      }
		}
    break;    
  case F_RIGHE:
    //se lo sheet ha ricevuto un se_enter, allora aggiorno i campi del dettaglio sulla mashera principale
    if (e == se_enter)
    {
      TSheet_field& ss = (TSheet_field&)o;
      TToken_string& row = ss.row(ss.selected());

      for (short id = F_CODART; id <= F_CODART+35; id++)
      {
        const int pos=id2pos(id);
        if (pos>0)
				{
          TMask_field& f = fld(pos);
          const TString& oldval = f.get();
          const char* newval = row.get(ss.cid2index(id - 400));
          if (oldval != newval)
          {
            f.set(newval);
            if (f.is_kind_of(CLASS_LIST_FIELD))
              f.on_hit();
          }
          f.set_dirty(false);
				}
      }
			if (e == se_enter || e == se_notify_modify ||	e == se_query_add || e == se_notify_add)
				highlight_row();

      //questo pezzo serve per gestire enable e disable dei campi in modo corretto
      //senza massage in maschera, sia sullo sheet che sul dettaglio
      TMask& rowmask = ss.sheet_mask();
      if (field(F_PPCONF).empty())
      {
        set(F_CALCCONS, "0");
        disable(F_CALCCONS);
        rowmask.set(S_CALCCONS, "0");
        rowmask.field(S_CALCCONS).disable();
        rowmask.field(S_CONG).disable();
      }
      else
      {
        enable(F_CALCCONS);
        rowmask.field(S_CALCCONS).enable();
        rowmask.field(S_CONG).enable();
      }
        
      _artrig = row.get(0);  //salvo nella variabile globale il codart della riga selezionata
    }
    //se ho cancellato una riga dello sheet, chiedo conferma che sia effettivamente quello che si vuole fare
    if (e == se_query_del)
    {
      TSheet_field& ss = (TSheet_field&)o;
      TToken_string& row = ss.row(ss.selected());
      const TString80 codart = row.get(ss.cid2index(S_CODART));
      if (codart.full())
      {
        const long dotin = row.get_long(ss.cid2index(S_DOTIN));
        if (dotin > 0)
          return error_box("Impossibile cancellare l'articolo %s perchè ha una dotazione iniziale non nulla", (const char*) codart);

        const TDate oggi(TODAY);
        const int danno = oggi.year() - 1;
        const int aanno = oggi.year() + 1;

        //controllo articolo in buoni di consegna
        const TString8 tipoela = ini_get_string(CONFIG_DITTA, "lv", "FatDif");
      
        const TString4 codnumbc  = ini_get_string(CONFIG_DITTA, "lv", "NUM_GEN");
        const TString4 tipodocbc = ini_get_string(CONFIG_DITTA, "lv", "TIPODOC_GEN");
        const char statofbc = cache().get("%ELD", tipoela, "S4").left(0)[0];
        const long codcf = get_long(F_CODCF);

        TString query;
        query << "USE RDOC KEY 5\n"
              << "SELECT (DOC.TIPODOC=\"" << tipodocbc << "\")&&(DOC.STATO<\"" << statofbc << "\")&&(DOC.CODCF=\"" << codcf << "\")\n"
              << "JOIN DOC INTO PROVV=PROVV ANNO=ANNO CODNUM=CODNUM NDOC=NDOC\n"
              << "FROM CODART=\"" << codart << "\" ANNO=" << danno << "CODNUM=\"" << codnumbc << "\"\n"
              << "TO CODART=\"" << codart << "\" ANNO=" << danno << "CODNUM=\"" << codnumbc << "\"\n";

        TISAM_recordset bcon(query);

        if(bcon.move_first())
        {
          TString str;
          str << "Non è possibile cancellare dal contratto l'articolo " << codart 
              << "in quanto è presente su " << bcon.items() << " buoni di consegna ancora da fatturare\n"
              << doc_rif(bcon);
          return error_box(str);
        }

        //controllo articolo in buoni di ritiro      
        const TString4 codnumbr = ini_get_string(CONFIG_DITTA, "lv", "NUM_RIT", NULL, 0);
        const TString4 tipodocbr = ini_get_string(CONFIG_DITTA, "lv", "TIPODOC_RIT", NULL, 0);
        const char statofbr = cache().get("%TIP", tipodocbr, "S2").mid(1,1)[0];

        query.cut(0);
        query << "USE RDOC KEY 5\n"
              << "SELECT (DOC.TIPODOC=\"" << tipodocbr << "\")&&(DOC.STATO<\"" << statofbr << "\")&&(DOC.CODCF=\"" << codcf << "\")\n"
              << "JOIN DOC INTO PROVV=PROVV ANNO=ANNO CODNUM=CODNUM NDOC=NDOC\n"
              << "FROM CODART=\"" << codart << "\" ANNO=" << danno << "CODNUM=\"" << codnumbr << "\"\n"
              << "TO CODART=\"" << codart << "\" ANNO=" << danno << "CODNUM=\"" << codnumbr << "\"\n";

        TISAM_recordset brit(query);

        if(brit.move_first())
        {
          TString str;
          str << "Non è possibile cancellare dal contratto l'articolo " << codart 
              << "in quanto è presente su " << brit.items() << " buoni di ritiro ancora da evadere\n"
              << doc_rif(brit);
          return error_box(str);
        }

        //controllo articolo in buoni di prelievo      
        const TString4 codnumbp = ini_get_string(CONFIG_DITTA, "lv", "NUM_PRE", NULL, 0);
        const TString4 tipodocbp = ini_get_string(CONFIG_DITTA, "lv", "TIPODOC_PRE", NULL, 0);
        const char statofbp = cache().get("%TIP", tipodocbp, "S2").mid(2,1)[0];

        query.cut(0);
        query << "USE RDOC KEY 5\n"
              << "SELECT (DOC.TIPODOC=\"" << tipodocbp << "\")&&(DOC.STATO<\"" << statofbp << "\")&&(DOC.CODCF=\"" << codcf << "\")\n"
              << "JOIN DOC INTO PROVV=PROVV ANNO=ANNO CODNUM=CODNUM NDOC=NDOC\n"
              << "FROM CODART=\"" << codart << "\" ANNO=" << danno << "CODNUM=\"" << codnumbp << "\"\n"
              << "TO CODART=\"" << codart << "\" ANNO=" << danno << "CODNUM=\"" << codnumbp << "\"\n";

        TISAM_recordset bpre(query);
        if(bpre.move_first())
        {
          TString str;
          str << "Non è possibile cancellare dal contratto l'articolo " << codart 
              << "in quanto è presente su " << bpre.items() << " buoni di prelievo ancora da evadere\n"
              << doc_rif(bpre);
          return error_box(str);
        }

        if (!noyes_box("Si desidera veramente cancellare l'articolo %s",(const char*) codart))
          return false;
      }
    }

    if (e == se_leave || e == se_notify_modify)
      _artrig.cut(0);

    break;
  case F_TIPOCAN:
  case F_NOLCICTE:
    {      
      //copio il valore del campo in questione della testata su tutte le righe se F_TIPOCAN vale
      //% su valore convenzionale per cliente, altrimente lascio quello che c'è
      const int tipocan = atoi(get(F_TIPOCAN));
      if (e == fe_modify || e == fe_init)
      {      
        TSheet_field& ss = sfield(F_RIGHE);
        
        //recupero le posizioni dei campi che devo modificare
        const int pos_tipoforf = ss.cid2index(S_TIPOFORF);
        const int pos_nolcic   = ss.cid2index(S_NOLCIC);
        const int pos_vcartcli = ss.cid2index(S_VCARTCLI);
        
        const char nolcicte = field(F_NOLCICTE).get()[0];

        if (tipocan == 2)
        {
          FOR_EACH_SHEET_ROW(ss, r, row)
          {          
            TToken_string& riga = ss.row(r);
            //scrivo i valori alle posizioni corrette
            riga.add(4, pos_tipoforf);
            riga.add(nolcicte, pos_nolcic);
            riga.add('C', pos_vcartcli);
            //disabilito le celle interessate
            ss.disable_cell(r, pos_tipoforf);
            ss.disable_cell(r, pos_nolcic);
            ss.disable_cell(r, pos_vcartcli);
          }          
          field(F_TIPOFORF).disable();
          field(F_NOLCIC).disable();
        }
        else
        {
          FOR_EACH_SHEET_ROW(ss, r, row)
          {          
            TToken_string& riga = ss.row(r);            
            //abilito le celle alle posizioni corrette            
            ss.enable_cell(r, pos_tipoforf);
            ss.enable_cell(r, pos_nolcic);
            ss.enable_cell(r, pos_vcartcli);
            if (riga.get_int(pos_tipoforf) == 4)
              riga.add('A', pos_vcartcli);
          }
          field(F_TIPOFORF).enable();
          field(F_NOLCIC).enable();
        }
        ss.force_update();
        if (ss.items() > 0) 
          ss.select(0); // Forza aggiornamento del dettaglio di riga (saponetta)
      }
    }
    break;
  case F_TIPOFORF:
    {
      //se questo campo risulta modificato, lo copio in alto e forzo l'update
      if (e == fe_modify/* || e == fe_init*/)
      {      
        TSheet_field& ss = sfield(F_RIGHE);
        const int sel = ss.selected();
        if (sel >= 0)
        {
          TToken_string& riga = ss.row(sel);
          riga.add(o.get(), ss.cid2index(S_TIPOFORF));
          if (ss.mask().get_int(F_TIPOCAN) != 2)
          {
            riga.add('A', ss.cid2index(S_VCARTCLI));
            ss.disable_cell(sel, ss.cid2index(S_VCARTCLI));
          }
          ss.force_update(sel);
        }
      }
    }
    break;
  case F_RITAUDTTMPRIG:
    {
      //obbligo a settare il flag prima in testata, altrimenti non lo lascio settare per le righe
      if (e == fe_modify || e == fe_init)
      {        
        if (o.get()[0] == 'X')
        {
          if (field(F_RITAUDTTMP).get()[0] != 'X')
          {
            warning_box("E' necessario prima attivare il ritiro automatico della dotazione temporanea in testata");
            o.set("");            
          }
        }

        if (e == fe_modify)
        {
          TSheet_field& ss = sfield(F_RIGHE);
          const int sel = ss.selected();

          if (sel >= 0)
          {
            const int index = ss.cid2index(S_RITAUDTTMP);        

            const char* oldval = ss.row(sel).get(index);
            const TString& newval = o.get();
            if (newval != oldval)
            {
              ss.sheet_mask().set(S_RITAUDTTMP,newval);
              ss.row(sel).add(newval,index);
              ss.force_update(sel);
            }
          }
        }
      }
    }
    break;
  case F_CALCCONS:
    //questo pezzo gestisce in maniera corretta gli enable e disable del campo conguaglio sullo sheet
    if (e == fe_modify)
    {
      TSheet_field& ss = sfield(F_RIGHE);
      const int sel = ss.selected();
      if (sel >= 0)
      {
        TToken_string& riga = ss.row(sel);
        riga.add(o.get(), ss.cid2index(F_CALCCONS));
        if (riga.get_long(ss.cid2index(S_PPCONF)) <= 0)
          ss.disable_cell(sel, ss.cid2index(S_CONG));
        else
        {
          if (atoi(o.get()) == 1)
            ss.enable_cell(sel, ss.cid2index(S_CONG));
          else
            ss.disable_cell(sel, ss.cid2index(S_CONG));
        }
      }
      ss.force_update(sel);      
    }
    break;
  case S_CODART:
    if (!o.empty())  //se il campo risulta pieno
    {
      if (e == fe_modify)  //e se risulta modificato
      {
        const TString& codart = o.get();
        TSheet_field& ss = sfield(F_RIGHE);
        TMask& m = o.mask(); // maschera di riga!

        const int rigasel = ss.selected();
        const TString80 art = o.get();
        
        FOR_EACH_SHEET_ROW(ss, r, riga)
        {
          if(r == rigasel)
            continue;

          const TString80 tmp = riga->get(0);
          if(tmp == art)
          {
            TString msg;
            msg << "L'articolo " << codart << " è già presente a contratto e non è possibile reinserirlo.";
            warning_box(msg);
            
            m.set(S_CODART, _artrig);
            TToken_string& row = ss.row(rigasel);
            row.add(_artrig, 0);
            ss.force_update(rigasel);

            return false;
          }
        }
        


        //se ho scritto un articolo diverso da quello che esisteva prima
        //e se si desidera veramente modificarlo, allora permetto la modifica
        //e forzo l'updatre della riga, altrimenti riscrivo l'articolo che c'era prima
        //e lascio tutto invariato
        if (codart != _artrig)  
        {
          TToken_string& row = ss.row(ss.selected());
          real prezzo(row.get(ss.cid2index(S_PREZZOST)));

          if (_artrig.empty() || noyes_box("Si desidera veramente modificare l'articolo %s",(const char*) _artrig))
          {
            _artrig = codart;      
            //PROPONI PREZZO
            TToken_string key;
            key.add('C');
            key.add(field(F_CODCF).get());
            const TRectype& cfven = cache().get(LF_CFVEN, key);
            bool trvlst = false;

            if (!cfven.empty())
            {
              //se è settata la categoria merceologica, leggo sia il listino che la cat merc, altrimenti solo il listino

              TString8 codlis = cfven.get(CFV_CODLIST);
              TString8 catven;
              if (!ini_get_bool(CONFIG_DITTA,"ve", "GESLISCV"))
                catven = "";
              else
                catven = cfven.get(CFV_CATVEN);

              //cerco il prezzo sul listino
              key.cut(0);
              key.add('L');     //tipo
              key.add(catven);  //catven
              key.add("");      //tipocf
              key.add("");      //codcf
              key.add(codlis);  //codlis
              key.add('A');     //tiporiga
              key.add(codart);  //codriga
              key.add("");      //um
              key.add("");      //nscagl
              const TRectype& rcondv = cache().get(LF_RCONDV, key);

              if (!rcondv.empty())
              {
                prezzo = rcondv.get_real(RCONDV_PREZZO);
                trvlst = true;
              }
            }        

            //se non ho trovato un listino, o se non c'è un listino impostato
            //propongo come prezzo il valore convenzionale
            if (!trvlst)
            {
              key.cut(0);
              key.add(codart);
              key.add(1);
              const TRectype& umart = cache().get(LF_UMART, key);
              m.set(S_PREZZOST, umart.get_real(UMART_PREZZO));
              if (umart.get(UMART_PREZZO).full())
              {
                prezzo = umart.get_real(UMART_PREZZO);
                trvlst = true;
              }
            }

            if (!trvlst && insert_mode())
              warning_box(TR("Non è stato trovato nessun prezzo da proporre"));
          }
          else
            m.set(S_CODART, _artrig);
          
          row.add(prezzo.string(), ss.cid2index(S_PREZZOST));
          row.add(_artrig, ss.cid2index(S_CODART));
          ss.force_update(ss.selected());
        }
        //se all'articolo è associata un'unità di misura, la propongo
        //in automatico e richiamo il metodo  (); altrimenti lo richiamo
        //solo se è arrivato un fe_init al campo 
				on_art_select(e);
      }
      else
        if (e == fe_init && (o.get() != _artrig))
          on_art_select(e);
    }
    break;
  case S_CONG:
  case S_DOTIN:
  case S_DOTOD:
  case S_DOTTMP:
    {
      if (e == fe_modify)
      {
        TString str;
        str << "ATTENZIONE: Una quantità risulta modificata a mano; dopo questa operazione i totali dei movimenti"
            <<  "di magazzino potrebbero non corrispondere ai numeri qui salvati";
        warning_box(str);

        TSheet_field& ss = sfield(F_RIGHE);
        TMask& m = o.mask(); // maschera di riga!

        const int rigasel = ss.selected();
        const long qta = o.get_long();

        m.set(o.dlg(), qta);

        TToken_string& row = ss.row(rigasel);
        row.add(qta, ss.cid2index(o.dlg()));
        //ss.force_update(rigasel);
      }
    }
    break;
  case DLG_PLANNING:
    //se viene premuto il bottone "Giri", lancia lv0500 (generatore automatico dei giri)
    if (e == fe_button && edit_mode())
    {
      TRelation_application& app = (TRelation_application&) main_app();
      app.get_relation()->read(_isequal,_unlock);
      TString str;
      str << "lv0 -4 " << get(F_CODCF) << " " << get(F_CODCONT);
      TExternal_app planning(str);
      planning.run();
      app.get_relation()->read(_isequal,_lock);
    }
    break;
  case DLG_NEWREC:
    if (e == fe_button)
    {
      //se sono in edit_mode, forzo l'uscita dal contratto attuale
      if (edit_mode())
      {
        send_key(K_ESC, 0);
        return false;
      }

      //se sono in query_mode e esiste già un cliente selzionato, allora richiamo il metodo LV_NEW_CONTRACT()
      //e calcolo il primo codcont libero proponendolo in automatico
      if (query_mode())
      {
        const long codcf = get_long(F_CODCF);
        if (codcf > 0)
        {
          const int indsped = get_int(F_INDSPED);
          const long codcont = lv_new_contract(codcf, indsped);
          if (codcont > 0)
            set(F_CODCONT, codcont);
        }
      }  
    }
    break;
  case DLG_RESET:
    if (e == fe_button && noyes_box(TR("Si è sicuri di voler azzerare i conguagli di questo contratto?")))
      azzera_conguaglio();
 default:
    //se sto modificando un campo con indice > 500 e diverso da F_CAUSLAVDESCRIG
    //allora forzo l'update dello sheet sulla riga selezionata
    if (e == fe_modify && is_running() && o.dlg() > 500/*&& o.dlg() != F_CAUSLAVDESCRIG*/)
    {
      TSheet_field& ss = sfield(F_RIGHE);
      const int sel = ss.selected();

      if (sel >= 0)
      {
        const short rowid = o.dlg() - 400;
        const int index = ss.cid2index(rowid);        

        const char* oldval = ss.row(sel).get(index);
        const TString& newval = o.get();
        if (newval != oldval)
        {
          ss.sheet_mask().set(rowid,newval);
          ss.row(sel).add(newval,index);
          ss.force_update(sel);
        }
      }
    }
    break;
  }
  return true;
}

int TContratti_msk::get_riga()
{
  return _riga;
}

bool TContratti_msk::set_riga(const int val)
{
  _riga = val;
  return true;
}

//Costruttore; nasconde o mostra il campo F_INDSPED a seconda di cosa è scritto in configurazione
TContratti_msk::TContratti_msk():TAutomask("lv0400a"), _post_contr(0)
{
  if (!ini_get_bool(CONFIG_DITTA, "lv", "Useindsp"))
    field(F_INDSPED).hide();
	TFilename ininame(source_file());

	ininame.ext("ini");

	TConfig prof(ininame.name(), "Colors");
	COLOR back;
	COLOR fore;

	prof.write_protect(true);
	for (int i = 0; ; i++)
	{
		const TString& name = prof.get("RuleName", NULL, i);
		if (name.full())
		{
			const TString& expr = prof.get("Rule", NULL, i);
      const TTypeexp type = prof.get_char("RuleType", NULL, i, 'N') == 'S' ? _strexpr : _numexpr ;
			back = prof.get_color("BgCol", NULL, i, NORMAL_BACK_COLOR);
			fore = prof.get_color("FgCol", NULL, i, NORMAL_COLOR);
			color_rules().add(new TColor_rule(name, expr, type, back, fore));
		}
		else 
			break;
	}
}

                                     //////////////////////////////
                                     ////    TCONTRATTI_APP    ////
                                     //////////////////////////////

//classe TContratti_app
class TContratti_app: public TRelation_application
{
	TContratti_msk* _msk;
	TRelation*      _rel;
  TString80       _codart;

private:
  void save_rows(const TMask& m);
  TString build_query(const TMask& m) const;
  int find_art(TSheet_field& s,const TString& art) const;

protected:
	virtual TMask* get_mask (int mode) {return _msk; }
	virtual TRelation* get_relation() const {return _rel;}

  virtual bool user_create();
  virtual bool user_destroy();
  virtual void on_config_change();                  //METODO VUOTO
  virtual bool get_next_key(TToken_string& key);    //METODO MAI UTILIZZATO?
  virtual int read(TMask& m);
  virtual int write(const TMask& m);
  virtual int rewrite(const TMask& m);
  virtual bool protected_record(TRectype & rec);    //METODO MAI UTILIZZATO?
  virtual bool remove();
  virtual void init_query_mode(TMask& m);
  virtual void init_insert_mode(TMask& m);
  virtual void init_modify_mode(TMask& m);
  bool elimina_planning(const long& codcont, const long& codcf) const;
  bool kill_planning (TISAM_recordset& selrighe) const;
	bool menu(MENU_TAG mt);
};

//SAVE_ROWS: questo metodo salva effettivamente le righe vislualizzate sullo sheet sul file
//LF_LVRCONDV e aggiorna e/o aggiunge record su LF_CLIFOGIAC
void TContratti_app::save_rows(const TMask& m)
{
  //instanzio un TISAM_recordset sulle righe contratto
  TISAM_recordset righeset(build_query(m));
  //instazio un TLocalisamfile partendo dal recordset che ho appena creato
  //(cioè su LF_LVRCONDV)
  TLocalisamfile& file = righeset.cursor()->file();

  //recupero lo sheet
  TSheet_field& righe = m.sfield(F_RIGHE);
  
  //scorro tutte le righe contratto e elimino tutte quelle che non ci sono più sullo sheet
  for (bool ok = righeset.move_first(); ok; ok = righeset.move_next())
  {
    const TString& art = righeset.get("CODART").as_string();
    if (find_art(righe, art) < 0)
       file.remove();
  }

  //instanzio un TLocalisamfile su LF_CLIFOGIAC
  TLocalisamfile magcli(LF_CLIFOGIAC);

  //setto alcune variabili di interesse
  const TDate oggi(TODAY);
  const int year = oggi.year();
  const long clifo = m.get_long(F_CODCF);
  const int indsp = m.get_int(F_INDSPED);

  TEsercizi_contabili& esc = esercizi();
  const int last_esc = esc.last();
  const int pred_esc = esc.pred(last_esc);

  const bool pred_esc_chiuso = esc[pred_esc].chiusura_mag().ok();
  const int esercizio = last_esc;

  //recupero la maschera di riga
  TMask& msk = righe.sheet_mask();

  //per ogni riga dello sheet
  FOR_EACH_SHEET_ROW(righe, r, row)
  {
    file.zero();
    file.put("CODCF",clifo);
    file.put("CODCONT",m.get(F_CODCONT));    

    //per ogni campo della maschera scrivi setta all'interno del record corrente di file
    //il valore di quei campi che hanno un field
    FOR_EACH_MASK_FIELD(msk,i,f)
    {
      const TFieldref* fr = f->field();
      if (fr != NULL)
      {
        const int pos = righe.cid2index(f->dlg());
        fr->write(row->get(pos), file.curr());
      }
    }

    //leggo il codart
    const TString80 codart = row->get(righe.cid2index(S_CODART));
    if (codart.full())
    {
      file.rewrite_write();
      //se il codart è pieno e le quantità sono modificabili (da configurazione)
      if (ini_get_bool(CONFIG_DITTA, "lv", "Qtamodi"))
      {
        TArticolo_lavanderie& artrec = cached_article_laundry(codart, 'C', clifo, indsp);
        TRecmag_lavanderie reclav(artrec.find_rec(esercizio));
        TRecmag_lavanderie reclav_prec(artrec.find_rec(pred_esc));
        
        //gestione LF_CLIFOGIAC
        if(reclav.empty())
        {
          reclav.put(CLIFOGIAC_TIPOCF, 'C');
          reclav.put(CLIFOGIAC_CODCF, clifo);
          reclav.put(CLIFOGIAC_ANNOES, esercizio);
          reclav.put(CLIFOGIAC_INDSPED, indsp);
          reclav.put(CLIFOGIAC_CODART, codart);
          reclav.put(CLIFOGIAC_NRIGA, 1);
        }

        //riscrivo la dotazione iniziale convertita nell'unità di misura principale
        real dotin = row->get(righe.cid2index(S_DOTIN));
        if(!pred_esc_chiuso)
          dotin -= reclav_prec.get_real(CLIFOGIAC_DOTIN);
        reclav.put(CLIFOGIAC_DOTIN, dotin);		      
            
        //riscrivo la dotazione odierna convertita nell'unità di misura principale
        real dotod = row->get(righe.cid2index(S_DOTOD));
        if(!pred_esc_chiuso)
          dotod -= reclav_prec.get_real(CLIFOGIAC_DOTOD);
        reclav.put(CLIFOGIAC_DOTOD, dotod);
        
        //riscrivo la dotazione temporanea convertita nell'unità di misura principale
        real dottm = row->get(righe.cid2index(S_DOTTMP));
        if(!pred_esc_chiuso)
          dottm -= reclav_prec.get_real(CLIFOGIAC_DOTTM);
        reclav.put(CLIFOGIAC_DOTTM, dottm);

        //riscrivo il consegnato anno convertito nell'unità di misura principale
        const real conan = row->get(righe.cid2index(S_CONSANNO));
        reclav.put(CLIFOGIAC_CONSANNO, conan);
        reclav.rewrite_write(magcli);
      }
    }
  } //fine FOR_EACH_ROW
}

//BUILD_QUERY: metodo che crea la query sulle righe contratti
//recuperando i dati di interesse dalla maschera
TString TContratti_app:: build_query(const TMask& m) const
{
  TString query = "";
  query << "USE LVRCONDV\n"
        << "FROM CODCF=" << m.get(F_CODCF) << " CODCONT=" << m.get(F_CODCONT) << "\n"
        << "TO CODCF=" << m.get(F_CODCF) << " CODCONT=" << m.get(F_CODCONT);
  return query; 
}

//FIND_ART: metodo che restituisce l'indice della riga dello sheet che contiene
//l'articolo desiderato (-1 se non lo trova)
int TContratti_app::find_art(TSheet_field& s, const TString& art) const
{
  int r = -1;
  //scorro le righe dello sheet partendo dall'ultima
  for (r = s.items()-1 ; r>=0 ; r--)
  {
    const char* codart = s.row(r).get(0);
    if (art == codart)
      break;
  }
  return r;
}

//USER_CREATE: metodo che crea TRelation e TContratti_msk solo se si è in un esercizio valido
//e che setta i comportamenti sulle righe aggiunte agli sheet
bool TContratti_app:: user_create() 
{
	//se gli sono stati passati più parametri, allora setto la variabile globale _codart...
  //...con il parametro desiderato, se no lo setto a stringa vuota
  if (argc() > 2)
  {
    _codart = argv(3);
    _codart.ltrim(2);
  }
  else
    _codart = "";
  
  const TDate oggi(TODAY);
	if (esercizi().date2esc(oggi) == 0)
		return error_box("Attenzione non esiste l'esercizio corrispondente al %s", oggi.string());

  _rel=new TRelation (LF_LVCONDV);
  _msk= new TContratti_msk;

  _msk->set_riga(-1);

  TSheet_field& ss = _msk->sfield(F_RIGHE);

	ss.set_auto_append(false);
  ss.set_append(false);

	return true;
}

//USER_DESTROY: metodo che distrugge le variabili globali alla fine dell'esecuzione
bool TContratti_app:: user_destroy() 
{
   delete _msk;
   delete _rel;
   return true;
}


//ON_CONFIG_CHANGE: per adesso un semplice segnaposto
void TContratti_app:: on_config_change()
{
}

//GET_NEXT_KEY: metodo che restituisce la chiave di ricerca sui contratti prendendo il prossimo codcont libero
bool TContratti_app:: get_next_key(TToken_string& key)
{ 
  const long cliente=_msk->get_long(F_CODCF);
  if (cliente <= 0)
    return false;

  key.add(F_CODCF);
  key.add(cliente);
  key.add(F_CODCONT);

  long codcont= lv_new_contract(cliente, _msk->get_int(F_INDSPED));
  key.add(codcont);

  return true; 
}

//READ: ridefinizione del metodo read() delle TRealtion_application
int TContratti_app::read(TMask& m) 
{
  //eseguo la read() standard
  int err = TRelation_application::read(m);
  //se la read va a buon fine
  if(err == NOERR)
  {
    //instanzio un TISAM_recordset sulle righe contratto
    TISAM_recordset righeset(build_query(m));
    const TRectype& rec = righeset.cursor()->curr();

    //instanzio un TLcalisamfile su LF_CLIFOGIAC
    TLocalisamfile magcli(LF_CLIFOGIAC);
    //setto alcune variabili di interesse

    //la data deve essere un esercizio; è da prendere sempre l'ultimo esercizio esistente
    TEsercizi_contabili esc;
    const int last_esc = esc.last();

    /*const TDate oggi(TODAY);
    const int year = oggi.year();*/
    const long clifo = m.get_long(F_CODCF);
    const int indsp = m.get_int(F_INDSPED);
    //recupero sheet e realtiva mashera di riga
    TSheet_field& righe = m.sfield(F_RIGHE);
    TMask& msk = righe.sheet_mask();
    righe.destroy();

    //per ogni riga dello sheet
    int pos = -1;
    for (bool ok = righeset.move_first(); ok; ok = righeset.move_next())
    {
      ++pos;
      TToken_string& row = righe.row(-1);
      //per ogni campo della maschera scrivi setta all'interno del record corrente di file
      //il valore di quei campi che hanno un field
      FOR_EACH_MASK_FIELD(msk,i,f)
      {
        const TFieldref*fr=f->field();
        if (fr!= NULL)
          row.add(fr->read(rec),righe.cid2index(f->dlg()));  
      }
      
      const TString80 codart(row.get(righe.cid2index(S_CODART)));

      //se non gli ho passato nessun codart, allora dico che voglio dare il focus alla prima riga dello sheet
      //aktrimenti dico che volgio dare il focus alla riga dello sheet che contiene l'articolo che gli ho passato
      if (_codart.blank())
        _msk->set_riga(0);
      else
        if (codart == _codart)
        {
          _msk->set_riga(pos);
          _codart.cut(0);
        }

      //estraggo il record corrispondente su LF_CLIFOGIAC	
      TArticolo_lavanderie& artrec = cached_article_laundry(codart, 'C', clifo, indsp);
      artrec.find_rec(0); //svuoto la cache a forza
      const TRecmag_lavanderie& reclav = artrec.find_rec(last_esc);
      //lettura dei dati da LF_CLIFOGIAC
      //se esiste il record su LF_CLIFOGIAC, recupero l'unità di misura dalla riga dello sheet selezionata
      //e setto i campi delle dotazioni e dei consegnati ai valori corretti riportati alla giusta unità di misura
      if (!reclav.empty())
	    {
        //calcolo dotazione iniziale, scritta sia sulla maschera che sullo sheet
		    const real dotin = reclav.get_real(CLIFOGIAC_DOTIN);
  			row.add(dotin.stringa(), righe.cid2index(S_DOTIN));
  
        //calcolo dotazione odierna, scritta sia sulla maschera che sullo sheet
		    const real dotod = reclav.get_real(CLIFOGIAC_DOTOD);
				row.add(dotod.stringa(), righe.cid2index(S_DOTOD));
        //calcolo dotazione temporanea, scritta sia sulla maschera che sullo sheet
		    const real dottmp = reclav.get_real(CLIFOGIAC_DOTTM);
				row.add(dottmp.stringa(), righe.cid2index(S_DOTTMP));        
      }
      //forzo una check_row
      righe.check_row(righe.items()-1, 3);
    }
  }
  return err;
}

//WRITE: ridefinizione del metodo write() delle TRelation_application
int TContratti_app::write(const TMask& m)
{
  //esegui la write standard
  int err = TRelation_application::write(m);
  //se va a buon fine esegui la save_rows() e ricorda all'utente i passaggi per planning
  if(err == NOERR) 
  {
    save_rows(m);
    warning_box(TR("Ricordarsi di inserire i passaggi per planning"));
  }
  return err;
}

//REWRITE: ridefinizione del metodo rewrite() delle TRelation_application
int TContratti_app::rewrite(const TMask& m)
{
  //esegui la rewrite standard
  int err = TRelation_application::rewrite(m);
  //se va a buon fine esegui la save_rows()
  if(err == NOERR)
    save_rows(m);
  return err;
}

//PROTECTED_RECORD: metodo che rendo un record non cancellabile
bool TContratti_app::protected_record(TRectype & rec)
{
  TLaundry_contract cont(rec);
  return !cont.can_be_deleted();
}

//REMOVE: ridefinizione del metodo remove() delle TRelation_application
bool TContratti_app::remove()
{
  //recupero i dati dalla testata del contratto prima di cancellarla
  const TRectype& tcont = get_relation()->curr();
  const int daanno = tcont.get_date(LVCONDV_DATAIN).year();
  const int aanno  = TDate(TODAY).year();
  const long clifo  = tcont.get_long(LVCONDV_CODCF);
  const long indsp  = tcont.get_long(LVCONDV_CODINDSP);

  //eseguo la remove standard
  bool ok = TRelation_application::remove();

  //se va a buon fine, elimino anche le righe contratto dal file specifico
  if(ok)
  {
    TISAM_recordset righeset(build_query(*_msk));
    TLocalisamfile& rcont = righeset.cursor()->file();
   
    //elimino le giacenze per cliente SOLO se le quantità sono modificabili
    if (ini_get_bool(CONFIG_DITTA, "lv", "Qtamodi"))
    {
      TLocalisamfile magcli(LF_CLIFOGIAC);

      for (bool ok = righeset.move_first(); ok; ok = righeset.move_next())
      {
        //con questo ciclo elimino tutte le giacenze per cliente dall'anno di inizio validità
        //del contratto all'anno attuale (essendo nel ciclo che scorre le righe contratto
        //elimino tutti gli articoli presenti per quel contratto)
        for (int y = daanno; y <= aanno; y++)
        {        
          magcli.put(CLIFOGIAC_ANNOES, y);
          magcli.put(CLIFOGIAC_TIPOCF, 'C');
          magcli.put(CLIFOGIAC_CODCF, clifo);
          magcli.put(CLIFOGIAC_INDSPED, indsp);
          magcli.put(CLIFOGIAC_CODART, righeset.get(LVRCONDV_CODART).as_string());
          magcli.put(CLIFOGIAC_NRIGA, 1);        
          magcli.remove();
        }
      }
      rcont.remove();
    }
    //elimino i planning esistenti per quel cliente - contratto
    elimina_planning(_msk->get_long(F_CODCONT),_msk->get_long(F_CODCF));
  }
  return ok;
}

//INIT_QUERY_MODE: ridefinizione del metodo init_query_mode() standard
void TContratti_app::init_query_mode(TMask& m)
{
  //abilita il campo F_RAGSOC se il campo F_CODCF è abilitato
  m.field(F_RAGSOC).enable(m.field(F_CODCF).enabled());
  m.reset();
}

//INIT_MODIFY_MODE: ridefinizione del metodo init_modify_mode() standard
void TContratti_app::init_modify_mode(TMask& m)
{
  //setto alcune variabili di interesse
  const TDate oggi(TODAY);
  const int year = oggi.year();
  const long clifo = m.get_long(F_CODCF);
  const int indsp = m.get_int(F_INDSPED);
  const TEsercizi_contabili escon;
  
  //instanzio un TLocaisamfile su LF_CLIFOGIAC
  TLocalisamfile magcli(LF_CLIFOGIAC);
  //recupero lo sheet
  TSheet_field& righe = m.sfield(F_RIGHE);

  //per ogni riga dello sheet aggiorno le dotazioni su LF_CLIFOGIAC
  //per tutti ????, se il record esiste già
  FOR_EACH_SHEET_ROW(righe,r,row)
  {
    const TString80 codart=row->get(righe.cid2index(S_CODART));
   // righe.disable_cell(r, righe.cid2index(S_CODART));
   // righe.disable_cell(r, righe.cid2index(S_DESCR));
   // righe.disable_cell(r, righe.cid2index(S_UM));

    for (int y = escon.last() + oggi.month() == 12; y >= year; y--)
    {
      magcli.put(CLIFOGIAC_ANNOES, y);
      magcli.put(CLIFOGIAC_TIPOCF, 'C');
      magcli.put(CLIFOGIAC_CODCF, clifo);
      magcli.put(CLIFOGIAC_INDSPED, indsp);
      magcli.put(CLIFOGIAC_CODART, codart);
      
      if (magcli.read() == NOERR)
      {
        row->add(magcli.get(CLIFOGIAC_DOTIN), righe.cid2index(S_DOTIN));
        row->add(magcli.get(CLIFOGIAC_DOTOD), righe.cid2index(S_DOTOD));
        row->add(magcli.get(CLIFOGIAC_DOTTM), righe.cid2index(S_DOTTMP));
        break;
      }
    }
  }
	((TContratti_msk &)m).highlight();
}

//INIT_INSERT_MODE: ridefinizione del metodo init_insert_mode() standard
void TContratti_app::init_insert_mode(TMask& m)
{
  //se esiste già un contratto in essere alla data odierna per questo cliente,
  //contrassegna quello attuale come "PROPOSTA"
  const long codcf = m.get_long(F_CODCF);
  const long indsp = m.get_int(F_INDSPED);
  const TDate oggi(TODAY);
  const long old_contr = lv_find_contract(codcf, indsp, oggi);
  const long cur_contr = m.get_long(F_CODCONT);
  if (old_contr > 0 && old_contr < cur_contr)
    m.set(F_PROPOSTA, "X");
}

//ELIMINA PLANNING: metodo che prepara il recordset sui planning con le righe da eliminare
bool TContratti_app::elimina_planning(const long& codcont, const long& codcf) const
{
  //creo il recordset
  TISAM_recordset selrighe("USE LVRCONSPLAN KEY 3\nFROM CODCF=#CODCF CODCONT=#CODCONT \nTO CODCF=#CODCF CODCONT=#CODCONT");
  //setto le variabili
  selrighe.set_var("#CODCF",codcf);
  selrighe.set_var("#CODCONT",codcont);
  
  //richiamo la funzione che effettivamente fa la cancellazione delle righe interessate
  kill_planning(selrighe);

  return true;
}

//KILL_PLANNING: metodo che effettivamente fa la cancellazione dei planning interessati
bool TContratti_app::kill_planning (TISAM_recordset& selrighe) const
{
  //se effettivamente ci sono delle righe da cancellare, allora le cancello
  const int righe = selrighe.items();
  if (righe > 0)
  {
    TProgind pi(righe, TR("Eliminazione planning in corso..."), true, true);
    TLocalisamfile& rplan = selrighe.cursor()->file();
    for (bool ok = selrighe.move_last(); ok; ok = selrighe.move_prev())
    {
	    if (!pi.addstatus(1))
		    break;
      rplan.remove();
    }    
  }
  return true;
}

bool TContratti_app::menu(MENU_TAG mt)
{             
  bool ok = true;
  if (mt == MENU_ITEM_ID(1))
  {
    if (_msk != NULL)
			_msk->sel_color();
  }
  else
    ok = TRelation_application::menu(mt);
  return ok;  
}


int lv0400(int argc, char* argv[])
{
  TContratti_app app;
  app.run (argc,argv,TR("Gestione contratti"));
  return 0;
}