// oggetto TCausale_magazzino
// oggetto TMov_Mag , multirecord del movimento di magazzino
// funzione di ricostruzione saldi

#include <diction.h> 
#include <progind.h> 
#include <recset.h> 
#include <utility.h> 

#include "clifogiac.h"
#include "mglib.h" 
#include "movmag.h"
#include <cfven.h>

#ifndef __CGLIB01_H
#include "../cg/cglib01.h"
#endif
#ifndef __DBLIB_H
#include "../db/dblib.h"
#endif

class TSaldo_mag : public TObject
{
	int _codes;
	TString8 _codmag;
  TCodice_articolo _codart;
	TString16 _livello;
	TString8 _codcaus;
  real _quant;
	real _valore;

public: 
	const int codes() const { return _codes;}
	const TString& codmag() const  { return _codmag; }
  const TCodice_articolo & codart() const { return _codart; }
  const TString& livello() const  { return _livello; }
	const TString& codcaus() const  { return _codcaus; }
  const real& quant() const  { return _quant; }
  const real& valore() const { return _valore; }
  void set(int codes, const char * codmag, const char * codart, const char * livello, const char * codcaus);
  int operator==(const TSaldo_mag&) const;
  
  virtual TObject* dup() const { return new TSaldo_mag(*this); }
  
	bool is_deletable() const { return _quant == ZERO && _valore == ZERO; }
	static TToken_string & key(const TRectype & head, const TRectype & row);
	void add_quant(const real & q, bool plus = true) { _quant = _quant + (plus ? q : -q); }
	void add_valore(const real & v, bool plus = true) { _valore = _valore + (plus ? v : -v); }
	void add(const real & q, const real & v, bool plus = true) { add_quant(q, plus); add_valore(v, plus); }
	void sub_quant(const real & q) { add_quant(q, false); }
	void sub_valore(const real & v) { add_valore(v, false); }
	void sub(const real & q, const real & v) { add(q, v, false); } 

	TSaldo_mag(int codes, const char * codmag, const char * codart, const char * livello, const char * codcaus) {set(codes, codmag, codart, livello, codcaus);}
	TSaldo_mag(const TRectype & head, const TRectype & row);
	TSaldo_mag(const TSaldo_mag & s);
  virtual ~TSaldo_mag() {}; 
};

void TSaldo_mag::set(int codes, const char * codmag, const char * codart, const char * livello, const char * codcaus)
{
	_codes = codes;
  _codmag = codmag;
  _codart = codart;
  _livello = livello;
	_codcaus = codcaus;
}

int TSaldo_mag::operator==(const TSaldo_mag & s) const
{
  return (_codes == s._codes) &&
				 (_codmag == s._codmag) &&
				 (_codart == s._codart) &&
				 (_livello == s._livello) &&
				 (_codcaus == s._codcaus);
}

TToken_string & TSaldo_mag::key(const TRectype & head, const TRectype & row)
{
	TToken_string& key = get_tmp_string();

	key.add(head.get_int(MOVMAG_ANNOES));
  key.add(row.get(RMOVMAG_CODMAG));
  key.add(row.get(RMOVMAG_CODART));
  key.add(row.get(RMOVMAG_LIVGIAC));
	const TString & c = row.get(RMOVMAG_CODCAUS);
	key.add(c.full() ? c : head.get(MOVMAG_CODCAUS));
  return key;
}

TSaldo_mag::TSaldo_mag(const TRectype & head, const TRectype & row)
{
	_codes = head.get_int(MOVMAG_ANNOES);
  _codmag = row.get(RMOVMAG_CODMAG);
  _codart = row.get(RMOVMAG_CODART);
  _livello = row.get(RMOVMAG_LIVGIAC);
	_codcaus = row.get(RMOVMAG_CODCAUS);
	if (_codcaus.blank())
		_codcaus = head.get(MOVMAG_CODCAUS);
}

TSaldo_mag::TSaldo_mag(const TSaldo_mag & s)
{
	set(s._codes, s._codmag, s._codart, s._livello, s._codcaus);
	_quant = s._quant;
	_valore = s._valore;
 }

class TSaldo_mag_clifo : public TObject
{
	int _codes;
	char _tipocf;
	TString8 _codcf;
	int _codindsp;
  TCodice_articolo _codart;
	TString16 _livello;
	TString8 _codcaus;
  real _quant;
	real _valore;

public: 
	const int codes() const { return _codes;}
	const char tipocf() const { return _tipocf;}
	const TString& codcf() const  { return _codcf; }
	const int codindsp() const { return _codindsp;}
  const TCodice_articolo & codart() const { return _codart; }
  const TString& livello() const  { return _livello; }
	const TString& codcaus() const  { return _codcaus; }
  const real& quant() const  { return _quant; }
  const real& valore() const { return _valore; }
  void set(int codes, char tipocf, const char * codcf, int codindsp, const char * codart, const char * livello, const char * codcaus);
  int operator==(const TSaldo_mag_clifo&) const;
  
  virtual TObject* dup() const { return new TSaldo_mag_clifo(*this); }
  
	bool is_deletable() const { return _quant == ZERO && _valore == ZERO; }
	static TToken_string & key(const TRectype & head, const TRectype & row);
	void add_quant(const real & q, bool plus = true) { _quant = _quant + (plus ? q : -q); }
	void add_valore(const real & v, bool plus = true) { _valore = _valore + (plus ? v : -v); }
	void add(const real & q, const real & v, bool plus = true) { add_quant(q, plus); add_valore(v, plus); }
	void sub_quant(const real & q) { add_quant(q, false); }
	void sub_valore(const real & v) { add_valore(v, false); }
	void sub(const real & q, const real & v) { add(q, v, false); } 

	TSaldo_mag_clifo(int codes, char tipocf, const char * codcf, int codindsp, const char * codart, const char * livello, const char * codcaus);
	TSaldo_mag_clifo(const TRectype & head, const TRectype & row);
	TSaldo_mag_clifo(const TSaldo_mag_clifo & s);
  virtual ~TSaldo_mag_clifo() {}; 
};

void TSaldo_mag_clifo::set(int codes, char tipocf, const char * codcf, int codindsp, const char * codart, const char * livello, const char * codcaus)
{
	_codes = codes;
	_tipocf = tipocf;
	_codcf = codcf;
  _codart = codart;
	_codindsp =	codindsp;
  _codart = codart;
  _livello = livello;
	_codcaus = codcaus;
}

int TSaldo_mag_clifo::operator==(const TSaldo_mag_clifo & s) const
{
  return (_codes == s._codes) &&
				 (_tipocf == s._tipocf) &&
				 (_codcf == s._codcf) &&
				 (_codindsp == s._codindsp) &&
				 (_codart == s._codart) &&
				 (_livello == s._livello) &&
				 (_codcaus == s._codcaus);
}

TToken_string & TSaldo_mag_clifo::key(const TRectype & head, const TRectype & row)
{
	TToken_string& key = get_tmp_string();

	key.add(head.get_int(MOVMAG_ANNOES));
	key.add(head.get(MOVMAG_TIPOCF));
	key.add(head.get(MOVMAG_CODCF));
	key.add(head.get_int(MOVMAG_CODINDSP));
  key.add(row.get(RMOVMAG_CODART));
  key.add(row.get(RMOVMAG_LIVGIAC));
	const TString & c = row.get(RMOVMAG_CODCAUS);
	key.add(c.full() ? c : head.get(MOVMAG_CODCAUS));
  return key;
}

TSaldo_mag_clifo::TSaldo_mag_clifo(const TRectype & head, const TRectype & row)
{
	_codes = head.get_int(MOVMAG_ANNOES);
	_tipocf = head.get_char(MOVMAG_TIPOCF);
	_codcf = head.get(MOVMAG_CODCF);
	_codindsp = head.get_int(MOVMAG_CODINDSP);
  _codart = row.get(RMOVMAG_CODART);
  _livello = row.get(RMOVMAG_LIVGIAC);
	_codcaus = row.get(RMOVMAG_CODCAUS);
	if (_codcaus.blank())
		_codcaus = head.get(MOVMAG_CODCAUS);
}

TSaldo_mag_clifo::TSaldo_mag_clifo(const TSaldo_mag_clifo & s)
{
	set(s._codes, s._tipocf, s._codcf, s._codindsp, s._codart, s._livello, s._codcaus);
	_quant = s._quant;
	_valore = s._valore;
 }

// ********************************
// TMov_mag  

TMov_mag::TMov_mag() : 
  TMultiple_rectype(LF_MOVMAG)
{   
  add_file(LF_RMOVMAG,"NRIG");
}

TMov_mag::TMov_mag(long numreg) :
  TMultiple_rectype(LF_MOVMAG)
{
  add_file(LF_RMOVMAG,"NRIG");
  TLocalisamfile movmag(LF_MOVMAG);
  put(MOVMAG_NUMREG, numreg);
  read(movmag); 
}

TMov_mag::~TMov_mag() 
{   
}

TCausale_magazzino& TMov_mag::causale(int row) const
{
	const TRectype & rowrec = body()[row];
	TString8 cod(rowrec.get(RMOVMAG_CODCAUS));

  if (cod.blank())
    cod = get(MOVMAG_CODCAUS);
  return cached_causale_magazzino(cod);
}

void TMov_mag::zero(char c)
{
  TMultiple_rectype::zero(c);
  _saldi_mag.destroy();   
  _saldi_mag_clifo.destroy();
}

// valuta il valore della chiave per un nuovo record
bool TMov_mag::renum()
{
  put(MOVMAG_NUMREG,get_next_key());
  return TRUE;
}

// copia la chiave dal file principale a quello delle righe
void TMov_mag::set_body_key(TRectype & rowrec)
{
  rowrec.put(RMOVMAG_NUMREG,get(MOVMAG_NUMREG));
} 

/*void TMov_mag::load_rows_file(int logicnum)
{
  CHECK(logicnum==LF_RMOVMAG,"L'unico file collegabile ai movimenti sono le righe"); 
  TMultiple_rectype::load_rows_file(logicnum);
}*/

int TMov_mag::read(TBaseisamfile& f, word op, word lockop)
{
  _saldi_mag.destroy();   
  _saldi_mag_clifo.destroy();
	
	const int err = TMultiple_rectype::read(f, op, lockop);

	if (err == NOERR)
		add_saldi(false);
	return err;
}
int TMov_mag::remove(TBaseisamfile& f) const 
{
  const int res = TMultiple_rectype::remove(f);
  if (res == NOERR) 
    ((TMov_mag *)this)->update_balances();  
  return res;
}


void TMov_mag::add_extrarows() const
{
  add_autorows();
  add_explrows();
//  if (add_explrows())
//    add_autorows();
}

bool  TMov_mag::add_autorows() const
{
  bool added=FALSE;
  TString8 codmag;
  
  // aggiunge le righe automatiche
  for (int r = rows(); r > 0; r--) 
  {        
    TRecord_array & b = body();             
    TRectype & row = b[r];
		const TCausale_magazzino& cau = causale(r);
    const TString& codcaus = cau.caus_collegata();

		if (codcaus.not_empty())
    {
      const TCausale_magazzino& cau_coll = cached_causale_magazzino(codcaus);
      
			if (!row.get_bool(RMOVMAG_ESPLOSA))
      {
        // deve esserci una riga collegata
        if (!b.exist(r + 1) || b[r + 1].get_char(RMOVMAG_TIPORIGA) != riga_automatica)
        {
          // manca, la inserisco
          TRectype * linea_auto = new TRectype(row);
          
          codmag = codmag_rauto(r);
          if (codmag.empty())
            codmag = cau_coll.default_magdep();
          if (codmag.not_empty())
            linea_auto->put(RMOVMAG_CODMAG, codmag);
          const char * prezzo = prezzo_rauto(r);
          if (prezzo != NULL)
            linea_auto->put(RMOVMAG_PREZZO, prezzo);
          linea_auto->put(RMOVMAG_NRIG, r+1);     
          linea_auto->put(RMOVMAG_TIPORIGA, (char) riga_automatica);
          linea_auto->put(RMOVMAG_CODCAUS, codcaus);
          b.insert_row(linea_auto);
          added=TRUE;
        }
      }
    }
  } // ciclo righe
  return added;
}

bool TMov_mag::add_explrows() const
{
  TDistinta_tree  distinta;
  TArray boom;
  bool added=FALSE;

  TRecord_array& b = body();             
  // aggiunge le righe da explosione distinta
  for (int r = rows(); r > 0; r--) 
  {        
    const TRectype& row = b[r];
		const TCausale_magazzino& cau = causale(r);

		if (cau.esplodente() && !row.get_bool(RMOVMAG_ESPLOSA))
    {
      const TCodice_articolo codart = row.get(RMOVMAG_CODART);
      const char tipo_costo = cau.get("S11")[0];
      const int livello = cau.get_int("I0");
      const TString4 umroot(row.get(RMOVMAG_UM));
      const int rigaprec = r - 1;
      const bool is_coll = r > 0 && b[r].get_char(RMOVMAG_TIPORIGA) == riga_automatica;
      // mancano le righe, le inserisco
      boom.destroy();
      bool ok=distinta.set_root(row);
      if (ok)
      { 
        bool stop_prod = cau.get_bool("B4");
        
        distinta.explode(boom, TRUE, RAGGR_EXP_NONE, livello, "A", 0, stop_prod);
        //TString codmag(codmag_rauto(r));
        real prezzo(prezzo_rauto(r));
        TRectype * linea_auto;
        for (int newrow=0; newrow < boom.items() ; newrow++)
        {       
          TRiga_esplosione & riga_esp=(TRiga_esplosione & )(boom[newrow]);
          linea_auto = new TRectype(row);
          linea_auto->put(RMOVMAG_CODART, riga_esp.articolo());
          linea_auto->put(RMOVMAG_LIVGIAC, riga_esp.giacenza());
          linea_auto->put(RMOVMAG_UM, riga_esp.um());
          linea_auto->put(RMOVMAG_QUANT, riga_esp.val());
          linea_auto->put(RMOVMAG_CODCAUS, cau.codice());
          //if (codmag.not_empty())
          //  linea_auto->put(RMOVMAG_CODMAG, codmag);
          //if (!prezzo.is_zero())
          const TArticolo & articolo = cached_article(riga_esp.articolo());
          if (tipo_costo == 'U')
            prezzo = articolo.get_real(ANAMAG_ULTCOS1);
          else
            if (tipo_costo == 'S')
              prezzo = articolo.get_real(ANAMAG_COSTSTD);
          linea_auto->put(RMOVMAG_PREZZO, prezzo);
          linea_auto->put(RMOVMAG_NRIG, r+1+newrow);
          linea_auto->put(RMOVMAG_ESPLOSA, TRUE);

          char coll_type = articolo.get_char(ANAMAG_COLLTYPE);
          TString8 codmag;
            
          if (coll_type == 'M')
          {
            if (is_coll)
              codmag = b[rigaprec].get(RMOVMAG_CODMAG);
          }
          else
            if (coll_type == 'F')
            {                         
              TString16 key("F|");                                
              key << articolo.get(ANAMAG_CODFORN);
              codmag = cache().get(LF_CFVEN, key, CFV_CODMAG);
            }
            else
              if (coll_type == 'A')
                codmag = articolo.get(ANAMAG_CODMAG);
          if (codmag.not_empty())
            linea_auto->put(RMOVMAG_CODMAG, codmag);
          b.insert_row(linea_auto);
          added=TRUE;
        }
        // ora ci sono, mi basta eliminare la riga "padre"
        if (boom.items() > 0 )
        {
          if (boom.items() == 1 && codart == ((TRiga_esplosione &)boom[0]).articolo())
            error_box(FR("Movimento di magazzino %ld:\ndistinta %s ciclica"), get_long(MOVMAG_NUMREG),(const char *)codart);
          b.destroy_row(r,TRUE);
        }
        else
          message_box(FR("Movimento di magazzino %ld:\nimpossibile esplodere l'articolo %s"), get_long(MOVMAG_NUMREG),(const char *)codart);
      }
			else 
				if (b[r].get_char(RMOVMAG_TIPORIGA) == riga_automatica)
					b.destroy_row(r,TRUE);

    }
  } // ciclo righe
  return added;
}

int TMov_mag::write(TBaseisamfile& f) const 
{
  int res;
  add_extrarows();
  if ((res=TMultiple_rectype::write(f))==NOERR ) 
  {
    TMov_mag &myself=((TMov_mag &)*this);
  
    myself.add_saldi();
		myself.update_balances();  
    myself.add_saldi(false);
  }
  return res;
}

int TMov_mag::rewrite(TBaseisamfile& f) const 
{
  int res;
  add_extrarows();
  if ((res=TMultiple_rectype::rewrite(f))==NOERR ) 
  {
    TMov_mag &myself=((TMov_mag &)*this);
    
    myself.add_saldi();
    myself.update_balances();  
    myself.add_saldi(false);
  }
  return res;
}

const char* TMov_mag::get_next_key()  
{
  TLocalisamfile f(LF_MOVMAG);
  f.last();
  long a = f.get_long(MOVMAG_NUMREG)+1;
  TString& nextcod = get_tmp_string();
  nextcod.format("%ld", a);
  return nextcod;
}

bool TMov_mag::key_complete()
{
  const bool ok = head().get_long(MOVMAG_NUMREG) > 0L;
  return ok;
}

//*******
// gestione delle variazione dei saldi
//
//
bool TMov_mag::force_update_bal()
{
  // reset delle strutture per il controlli delle variazioni dei saldi
	const TRecord_array & b = body();

	_saldi_mag.destroy();
	_saldi_mag_clifo.destroy();
  add_saldi();
  return update_balances(false);  
}

void TMov_mag::renum_mov(const long numreg)
{
	put(MOVMAG_NUMREG, numreg);
	renum_key();
}

// restituisce il valore dei dati

void TMov_mag::add_saldi(const bool plus)
{
  const TRecord_array& b = body();

  for (int i = b.last_row(); i > 0; i = b.pred_row(i))
  {
		const TRectype & rec = b[i];
		TToken_string & key_mag = TSaldo_mag::key(*this, rec);
		TSaldo_mag * s_mag = (TSaldo_mag*) _saldi_mag.objptr(key_mag);

		if (s_mag == NULL)
		{
			s_mag = new TSaldo_mag(*this, rec);
			_saldi_mag.add(key_mag, s_mag);
		}

		real quant = rec.get_real(RMOVMAG_QUANT);
	  const TCausale_magazzino& caus = causale(i);
		TArticolo & art = articolo(i);
		
		quant =art.convert_to_um(quant, NULL, rec.get(RMOVMAG_UM));
		
		real valore = (quant.is_zero() && caus.update_val()) ? rec.get_real(RMOVMAG_PREZZO) : rec.get_real(RMOVMAG_PREZZO) * quant;
		
		s_mag->add(quant, valore, plus);

		if (caus.aggiorna_clifo()&& get_long(MOVMAG_CODCF) > 0L)
		{        
			TToken_string & key_clifo = TSaldo_mag_clifo::key(*this, rec);
			TSaldo_mag_clifo * s_clifo = (TSaldo_mag_clifo*) _saldi_mag_clifo.objptr(key_clifo);

			if (s_clifo == NULL)
			{
				s_clifo = new TSaldo_mag_clifo(*this, rec);
				_saldi_mag_clifo.add(key_clifo, s_clifo);
			}
			s_clifo->add(quant, valore, plus);
		}
  }
}


bool TMov_mag::unlock_anamag(const char *codart) 
{
  TLocalisamfile anamag(LF_ANAMAG);
  
  anamag.put(ANAMAG_CODART,codart);
  return (anamag.read(_isequal,_unlock)==NOERR);
}

bool TMov_mag::lock_anamag(const char *codart) 
{
  TLocalisamfile anamag(LF_ANAMAG);
  anamag.put(ANAMAG_CODART,codart);
  
  KEY key = K_ENTER;
  while (key != K_ESC)
  {
    if (anamag.read(_isequal,_testandlock)==NOERR) 
      return TRUE;

    TString mess;
    mess << TR("Il record di anagrafica dell'articolo '")<< codart << TR("' risulta essere gi� in uso.");
    TTimed_breakbox bbox(mess,10);

    key = bbox.run();  
  }
  return FALSE;
}


void TMov_mag::giac_putkey(TRectype& mag, const TSaldo_mag & s) 
{
  mag.zero();    
  mag.put(MAG_ANNOES, s.codes());
  mag.put(MAG_CODMAG, s.codmag());
  mag.put(MAG_CODART, s.codart());
  mag.put(MAG_LIVELLO, s.livello());
}

void TMov_mag::giac_putkey_clifo(TRectype& clifomag, const TSaldo_mag_clifo & s)
{
  clifomag.zero();    
  clifomag.put(CLIFOGIAC_ANNOES, s.codes());
  clifomag.put(CLIFOGIAC_TIPOCF, s.tipocf());
  clifomag.put(CLIFOGIAC_CODCF, s.codcf());
  clifomag.put(CLIFOGIAC_INDSPED, s.codindsp());
  clifomag.put(CLIFOGIAC_CODART, s.codart());
  clifomag.put(CLIFOGIAC_LIVELLO, s.livello());
}

// aggiorna tutti i saldi in base alle modifiche fatte.
// il lock su anagrafica dovrebbe garantire il lock su tutte le giacenze dell'articolo
bool TMov_mag::update_balances(bool lock) 
{
  bool updated_bal = true;

  TLocalisamfile mag(LF_MAG);
  mag.setkey(2);
  TRectype& magcurr = mag.curr();

  const TRecord_array& b = body();
	const TString8 hcodcaus = get(MOVMAG_CODCAUS);

  for (int i = b.last_row(); i > 0; i = b.pred_row(i))
    if (causale(i).update_ultcos())
    {        
			const TRectype & rec = b[i];
			TArticolo & art = articolo(i);
			if (art.lock_and_prompt(lock ? _testandlock : _nolock))
			{
				const real prezzo = art.convert_to_um(rec.get_real(RMOVMAG_PREZZO), NULL, rec.get(RMOVMAG_UM), false);
       	const long numreg = get_long(MOVMAG_NUMREG);
				art.update_ultcosti(prezzo,get_date(MOVMAG_DATACOMP), numreg, i);
				art.rewrite();
			}
			else
      {
				if (lock)
					art.unlock();
      }
		}       

  if (_saldi_mag.items() > 0)
	{
		TString_array keys_mag;

		_saldi_mag.get_keys(keys_mag);
		keys_mag.sort();
		
		for (TToken_string* curr_key = (TToken_string*)keys_mag.first_item();
				 curr_key != NULL; curr_key = (TToken_string*)keys_mag.succ_item())
		{
			const TSaldo_mag& saldo = (const TSaldo_mag&)_saldi_mag[*curr_key];
      const TCodice_articolo& codart = saldo.codart();
			TArticolo_giacenza& art = cached_article_balances(codart);

			if (art.lock_and_prompt(lock ? _testandlock : _nolock))
			{
				giac_putkey(magcurr, saldo);
        int err = mag.read();
				if (err != NOERR)
				{
          TRecord_array& sld = art.mag(saldo.codes());
          const int nriga = sld.rows() + 1;
					giac_putkey(magcurr, saldo);
					magcurr.put(MAG_NRIGA, nriga);
          sld.add_row(magcurr);
          err = mag.write();
				}
				update_balances(magcurr, saldo);
				err = mag.rewrite();
        if (err != NOERR)
          updated_bal = false;

				if (lock)
					art.unlock();
			}       
		} 
	}

	if (_saldi_mag_clifo.items() > 0)
	{
    TLocalisamfile clifomag(LF_CLIFOGIAC);
    clifomag.setkey(2);
    TRectype& clifomag_curr = clifomag.curr();

		TString_array keys_clifo;

		_saldi_mag_clifo.get_keys(keys_clifo);
		keys_clifo.sort();

		for (TToken_string* curr_key = (TToken_string*)keys_clifo.first_item();
				 curr_key != NULL; curr_key = (TToken_string*)keys_clifo.succ_item())      
		{ 
			TSaldo_mag_clifo & saldo=(TSaldo_mag_clifo &)_saldi_mag_clifo[*curr_key];

			TArticolo_giacenza & art = cached_article_balances(saldo.codart());
	    
			if (art.lock_and_prompt(lock ? _testandlock : _nolock))
			{
				giac_putkey_clifo(clifomag_curr, saldo);
				if (clifomag.read() != NOERR)
				{
					// non trovato: aggiungo
					clifomag.setkey(1);
					giac_putkey_clifo(clifomag_curr, saldo);
					clifomag_curr.put(CLIFOGIAC_NRIGA, 999);
					if (clifomag.read(_isgteq) == NOERR)
						clifomag.prev();
					int nriga = 1;
					if (clifomag_curr.get_int(CLIFOGIAC_ANNOES) == saldo.codes() &&
							clifomag_curr.get_char(CLIFOGIAC_TIPOCF) == saldo.tipocf() &&
							clifomag_curr.get(CLIFOGIAC_CODCF) == saldo.codcf() &&
							clifomag_curr.get(CLIFOGIAC_CODART) == saldo.codart() &&
							clifomag_curr.get(CLIFOGIAC_LIVELLO ) == saldo.livello())
						nriga = clifomag_curr.get_int(CLIFOGIAC_NRIGA) + 1;
					giac_putkey_clifo(clifomag_curr, saldo);
					clifomag_curr.put(CLIFOGIAC_NRIGA, nriga);
					clifomag.write();
					clifomag.setkey(2);
				} 
				update_balances_clifo(clifomag_curr, saldo);
				clifomag.rewrite();

				if (lock)
					art.unlock();
			}
		}
	}
	_saldi_mag.destroy();
	_saldi_mag_clifo.destroy();
  return updated_bal;
}

// aggiorna i saldi del record corrente 
// in base alla causale e alla modifica fatta (con segno + o -)
void TMov_mag::update_balances(TRectype & magrec, const TSaldo_mag & s) 
{
  const TCausale_magazzino& caus = cached_causale_magazzino(s.codcaus());
  
  if (caus.update_qta())
  {
		const real diff = s.quant();

    update_balance(magrec, MAG_GIAC, diff, caus.sgn(s_giac)); // update ..
    update_balance(magrec, MAG_ACQ, diff, caus.sgn(s_acq)); // update ..
    update_balance(magrec, MAG_ENT, diff, caus.sgn(s_ent));
    update_balance(magrec, MAG_VEN, diff, caus.sgn(s_ven));
    update_balance(magrec, MAG_USC, diff, caus.sgn(s_usc));
    update_balance(magrec, MAG_ORDC, diff, caus.sgn(s_ordc));
    update_balance(magrec, MAG_ORDF, diff, caus.sgn(s_ordf));
    update_balance(magrec, MAG_RIM, diff, caus.sgn(s_rim));
    update_balance(magrec, MAG_SCARTI, diff, caus.sgn(s_scart));
    update_balance(magrec, MAG_INCL, diff, caus.sgn(s_incl));
    update_balance(magrec, MAG_ACL, diff, caus.sgn(s_acl));
    update_balance(magrec, MAG_PRODCOMP, diff, caus.sgn(s_prodc));
    update_balance(magrec, MAG_PRODFIN, diff, caus.sgn(s_prodf));
    update_balance(magrec, MAG_NLABEL, diff, caus.sgn(s_label));
    update_balance(magrec, MAG_USER1, diff, caus.sgn(s_user1));
    update_balance(magrec, MAG_USER2, diff, caus.sgn(s_user2));
    update_balance(magrec, MAG_USER3, diff, caus.sgn(s_user3));
    update_balance(magrec, MAG_USER4, diff, caus.sgn(s_user4));
    update_balance(magrec, MAG_USER5, diff, caus.sgn(s_user5));
    update_balance(magrec, MAG_USER6, diff, caus.sgn(s_user6));
  }
  if (caus.update_val())
  {
		const real diff_val = s.valore();

    update_balance(magrec, MAG_VALACQ, diff_val, caus.sgn(s_acq)); // update ..
    update_balance(magrec, MAG_VALENT, diff_val, caus.sgn(s_ent));
    update_balance(magrec, MAG_VALVEN, diff_val, caus.sgn(s_ven));
    update_balance(magrec, MAG_VALUSC, diff_val, caus.sgn(s_usc));
    update_balance(magrec, MAG_VALORDC, diff_val, caus.sgn(s_ordc));
    update_balance(magrec, MAG_VALORDF, diff_val, caus.sgn(s_ordf));
    update_balance(magrec, MAG_VALRIM, diff_val, caus.sgn(s_rim));
    update_balance(magrec, MAG_VALSCARTI, diff_val, caus.sgn(s_scart));
    update_balance(magrec, MAG_USERVAL1, diff_val, caus.sgn(s_user1));
    update_balance(magrec, MAG_USERVAL2, diff_val, caus.sgn(s_user2));
    update_balance(magrec, MAG_USERVAL3, diff_val, caus.sgn(s_user3));
    update_balance(magrec, MAG_USERVAL4, diff_val, caus.sgn(s_user4));
    update_balance(magrec, MAG_USERVAL5, diff_val, caus.sgn(s_user5));
    update_balance(magrec, MAG_USERVAL6, diff_val, caus.sgn(s_user6));
  }
}

// aggiorna i saldi del record corrente 
// in base alla causale e alla modifica fatta (con segno + o -)
void TMov_mag::update_balances_clifo(TRectype & clifomagrec, const TSaldo_mag_clifo & s)
{
  const TCausale_magazzino& caus = cached_causale_magazzino(s.codcaus());

	if (caus.update_qta())
  {
		const real diff = s.quant();

    update_balance(clifomagrec, CLIFOGIAC_GIAC, -diff, caus.sgn(s_giac)); // update ..
    update_balance(clifomagrec, CLIFOGIAC_ACQ, diff, caus.sgn(s_ven)); // update ..
    update_balance(clifomagrec, CLIFOGIAC_ENT, diff, caus.sgn(s_usc));
    update_balance(clifomagrec, CLIFOGIAC_VEN, diff, caus.sgn(s_acq));
    update_balance(clifomagrec, CLIFOGIAC_USC, diff, caus.sgn(s_ent));
    update_balance(clifomagrec, CLIFOGIAC_ORDC, diff, caus.sgn(s_ordf));
    update_balance(clifomagrec, CLIFOGIAC_ORDF, diff, caus.sgn(s_ordc));
    update_balance(clifomagrec, CLIFOGIAC_RIM, -diff, caus.sgn(s_rim));
    update_balance(clifomagrec, CLIFOGIAC_SCARTI, -diff, caus.sgn(s_scart));
    update_balance(clifomagrec, CLIFOGIAC_INCL, diff, caus.sgn(s_acl));
    update_balance(clifomagrec, CLIFOGIAC_ACL, diff, caus.sgn(s_incl));
    update_balance(clifomagrec, CLIFOGIAC_PRODCOMP, -diff, caus.sgn(s_prodc));
    update_balance(clifomagrec, CLIFOGIAC_PRODFIN, -diff, caus.sgn(s_prodf));
    update_balance(clifomagrec, CLIFOGIAC_DOTIN, diff, caus.sgn(s_dotin));
    update_balance(clifomagrec, CLIFOGIAC_DOTOD, diff, caus.sgn(s_dotod));
    update_balance(clifomagrec, CLIFOGIAC_DOTTM, diff, caus.sgn(s_dottm));
    update_balance(clifomagrec, CLIFOGIAC_CONSANNO, diff, caus.sgn(s_consanno));
    update_balance(clifomagrec, CLIFOGIAC_USER1, diff, caus.sgn(s_user1));
    update_balance(clifomagrec, CLIFOGIAC_USER2, diff, caus.sgn(s_user2));
    update_balance(clifomagrec, CLIFOGIAC_USER3, diff, caus.sgn(s_user3));
    update_balance(clifomagrec, CLIFOGIAC_USER4, diff, caus.sgn(s_user4));
    update_balance(clifomagrec, CLIFOGIAC_USER5, diff, caus.sgn(s_user5));
    update_balance(clifomagrec, CLIFOGIAC_USER6, diff, caus.sgn(s_user6));
  }
  if (caus.update_val())
  {
		const real diff_val = s.valore();

    update_balance(clifomagrec, CLIFOGIAC_VALACQ, diff_val, caus.sgn(s_ven)); // update ..
    update_balance(clifomagrec, CLIFOGIAC_VALENT, diff_val, caus.sgn(s_usc));
    update_balance(clifomagrec, CLIFOGIAC_VALVEN, diff_val, caus.sgn(s_acq));
    update_balance(clifomagrec, CLIFOGIAC_VALUSC, diff_val, caus.sgn(s_ven));
    update_balance(clifomagrec, CLIFOGIAC_VALORDC, diff_val, caus.sgn(s_ordf));
    update_balance(clifomagrec, CLIFOGIAC_VALORDF, diff_val, caus.sgn(s_ordc));
    update_balance(clifomagrec, CLIFOGIAC_VALRIM, -diff_val, caus.sgn(s_rim));
    update_balance(clifomagrec, CLIFOGIAC_VALSCARTI, -diff_val, caus.sgn(s_scart));
    update_balance(clifomagrec, CLIFOGIAC_USERVAL1, diff_val, caus.sgn(s_user1));
    update_balance(clifomagrec, CLIFOGIAC_USERVAL2, diff_val, caus.sgn(s_user2));
    update_balance(clifomagrec, CLIFOGIAC_USERVAL3, diff_val, caus.sgn(s_user3));
    update_balance(clifomagrec, CLIFOGIAC_USERVAL4, diff_val, caus.sgn(s_user4));
    update_balance(clifomagrec, CLIFOGIAC_USERVAL5, diff_val, caus.sgn(s_user5));
    update_balance(clifomagrec, CLIFOGIAC_USERVAL6, diff_val, caus.sgn(s_user6));
  }
}

void TMov_mag::update_balances(TRectype& magrec, int numrig, bool plus) 
{
	const TRectype & rec = body()[numrig];
  TSaldo_mag saldo(*this, rec);
	real quant = rec.get_real(RMOVMAG_QUANT);
  const TCausale_magazzino& caus = cached_causale_magazzino(saldo.codcaus());
	TArticolo & art = articolo(numrig);

	quant = art.convert_to_um(quant, NULL, rec.get(RMOVMAG_UM));

	real valore = (quant.is_zero() && caus.update_val()) ? rec.get_real(RMOVMAG_PREZZO) : rec.get_real(RMOVMAG_PREZZO) * quant;
		
	saldo.add(quant, valore, plus);

  return update_balances(magrec, saldo);
}

void TMov_mag::update_balances_clifo(TRectype& cliforec, int numrig, bool plus) 
{
	const TRectype & rec = body()[numrig];
  TSaldo_mag_clifo saldo(*this, rec);
	real quant = rec.get_real(RMOVMAG_QUANT);
  const TCausale_magazzino& caus = cached_causale_magazzino(saldo.codcaus());
	TArticolo & art = articolo(numrig);
			
	quant = art.convert_to_um(quant, NULL, rec.get(RMOVMAG_UM));

	real valore = (quant.is_zero() && caus.update_val()) ? rec.get_real(RMOVMAG_PREZZO) : rec.get_real(RMOVMAG_PREZZO) * quant;
		
	saldo.add(quant, valore, plus);

  return update_balances_clifo(cliforec, saldo);
}

int TMov_mag::codice_esercizio(const TDate &d)
{
  return esercizi().date2esc(d);
}

void TMov_mag::update_balance(TRectype & rec, const char * fieldname, const real& val, const int sgn) const
{
  if (sgn != 0)
  {
    if (sgn > 0)
      rec.add(fieldname, val);
    else
      rec.add(fieldname, -val);
  }
}

struct TBalance_params
{
	bool zero_giac;
	bool closed;
	int codes;
	int codesprec;
	int esprec;
	TTipo_valorizz tipov;
	const char * catv;
	const char * codl;
};

HIDDEN bool reset_giac(const TRelation& rel, void* pJolly)
{
	TArticolo_giacenza & articolo = (TArticolo_giacenza &) rel.lfile().curr();
	TBalance_params & p = *((TBalance_params *)pJolly);

  if (p.zero_giac)
    articolo.azzera_saldi(p.codes, p.codesprec);
  else
    articolo.riporta_saldi(p.codesprec, p.codes, p.tipov, p.catv, p.codl);
	return true;
}

void reset_clifogiac(TRectype & rec, const TRectype & oldrec, bool closed)
{
	if (!oldrec.empty())
	{
	  const real acq = oldrec.get_real(CLIFOGIAC_ACQ) +  oldrec.get_real(CLIFOGIAC_RIM);
		const real valacq = oldrec.get_real(CLIFOGIAC_VALACQ) + oldrec.get_real(CLIFOGIAC_VALRIM);                       
		real val = acq.is_zero() ? ZERO : valacq / acq;
    const TPrice p(val);
    real rim;
		
		rim += oldrec.get_real(CLIFOGIAC_GIAC); 
    rim += oldrec.get_real(CLIFOGIAC_PRODFIN);
    rim -= oldrec.get_real(CLIFOGIAC_PRODCOMP);
    rim += oldrec.get_real(CLIFOGIAC_ACL);
    rim -= oldrec.get_real(CLIFOGIAC_INCL);

    const real valrim = p.get_num() * rim;
		const TCurrency c(valrim); // Arrontonda alla valuta

    rec.put(CLIFOGIAC_RIM, rim); rec.put(CLIFOGIAC_VALRIM, c.get_num());
    rec.zero(CLIFOGIAC_ACQ); rec.zero(CLIFOGIAC_VALACQ);
    rec.zero(CLIFOGIAC_ENT); rec.zero(CLIFOGIAC_VALENT);
    rec.zero(CLIFOGIAC_VEN); rec.zero(CLIFOGIAC_VALVEN);
    rec.zero(CLIFOGIAC_USC); rec.zero(CLIFOGIAC_VALUSC);
    rec.zero(CLIFOGIAC_SCARTI);
    if (!riporta_ordinato() || !closed)
    {
      rec.zero(CLIFOGIAC_ORDF);
			rec.zero(CLIFOGIAC_VALORDF);
      rec.zero(CLIFOGIAC_ORDC);
			rec.zero(CLIFOGIAC_VALORDC);
    }
		if (closed)
		{
	    rec.put(CLIFOGIAC_DOTOD, oldrec.get(CLIFOGIAC_DOTOD));
		  rec.put(CLIFOGIAC_DOTIN, oldrec.get(CLIFOGIAC_DOTIN));
			rec.put(CLIFOGIAC_DOTTM, oldrec.get(CLIFOGIAC_DOTTM));
		}
		else
		{
			rec.zero(CLIFOGIAC_DOTOD);
	    rec.zero(CLIFOGIAC_DOTIN);
		  rec.zero(CLIFOGIAC_DOTTM);
		}
  }
	else
	{
    for (int i = 0; zero_fields[i]; i++)
      rec.zero(zero_fields[i]);
    rec.zero(CLIFOGIAC_DOTIN);
    rec.zero(CLIFOGIAC_DOTOD);
    rec.zero(CLIFOGIAC_DOTTM);
	}
  rec.zero(CLIFOGIAC_CONSANNO);
}

void update_clifogiac(TRectype & rec, const TRectype & oldrec)
{
	if (!oldrec.empty())
	{
	  const real acq = oldrec.get_real(CLIFOGIAC_ACQ) +  oldrec.get_real(CLIFOGIAC_RIM);
		const real valacq = oldrec.get_real(CLIFOGIAC_VALACQ) + oldrec.get_real(CLIFOGIAC_VALRIM);                       
		real val = acq.is_zero() ? ZERO : valacq / acq;
    const TPrice p(val);
    const real rim_prec = rec.get_real(CLIFOGIAC_RIM);
    real rim = oldrec.get_real(CLIFOGIAC_GIAC);
		
    rim += oldrec.get_real(CLIFOGIAC_PRODFIN);
    rim -= oldrec.get_real(CLIFOGIAC_PRODCOMP);
    rim += oldrec.get_real(CLIFOGIAC_ACL);
    rim -= oldrec.get_real(CLIFOGIAC_INCL);

    const real valrim = p.get_num() * rim;
		const TCurrency c(valrim); // Arrontonda alla valuta

    rec.put(CLIFOGIAC_RIM, rim); rec.put(CLIFOGIAC_VALRIM, c.get_num());
		rec.add(CLIFOGIAC_GIAC, rim - rim_prec); 
    if (riporta_ordinato())
    {
      rec.add(CLIFOGIAC_ORDF, oldrec.get_real(CLIFOGIAC_ORDF));
			rec.add(CLIFOGIAC_VALORDF, oldrec.get_real(CLIFOGIAC_VALORDF));
      rec.add(CLIFOGIAC_ORDC, oldrec.get_real(CLIFOGIAC_ORDC));
			rec.add(CLIFOGIAC_VALORDC, oldrec.get_real(CLIFOGIAC_VALORDC));
    }
	  rec.add(CLIFOGIAC_DOTOD, oldrec.get_real(CLIFOGIAC_DOTOD));
		rec.add(CLIFOGIAC_DOTIN, oldrec.get_real(CLIFOGIAC_DOTIN));
		rec.add(CLIFOGIAC_DOTTM, oldrec.get_real(CLIFOGIAC_DOTTM));
  }
}

HIDDEN bool rel_reset_clifogiac(const TRelation& rel, void* pJolly)
{
	TRectype & rec = rel.curr();
	TBalance_params & p = *((TBalance_params *)pJolly);
	TRectype newrec(rec);

	newrec.put(CLIFOGIAC_ANNOES, p.codes);
	if (newrec.read(rel.lfile()) != NOERR)
	{
		newrec = rec;
		newrec.put(CLIFOGIAC_ANNOES, p.codes);
	}
	reset_clifogiac(newrec, rec, p.closed);
	return newrec.write_rewrite(rel.lfile()) == NOERR;
}

bool recalc_mov(const TRelation& rel, void* pJolly)
{
	TMov_mag & mov_rec = (TMov_mag &) rel.lfile().curr();
	bool & ok = *((bool *) pJolly);

  ok = mov_rec.force_update_bal();
	return true;
}

bool rebuild_balances(int codes, const TTipo_valorizz tipo_valorizz,
											const char* catven, const char* codlis)
{
	TSystemisamfile a(LF_MOVMAG);

	if (a.open(_excllock) != NOERR)
		return false;

  TBalance_params p;
	
	p.codes = codes;
	p.codesprec = esercizi().pred(codes);
	//p.zero_giac = p.codesprec > 0 && esercizi().esercizio(p.codesprec).chiusura_mag().ok();
  p.closed = esercizi().esercizio(p.codesprec).chiusura_mag().ok();
  p.zero_giac = p.codesprec <= 0 || esercizi().esercizio(p.codesprec).chiusura_mag().ok();
	p.catv = catven;
	p.codl = codlis;
	p.tipov = tipo_valorizz;
	
  // azzera tutte giacenze (ciclo sulle giacenze)
	TCursor anamag_cur(new TRelation(LF_ANAMAG));
	TString msg;
  
	anamag_cur.relation()->lfile().set_curr(new TArticolo_giacenza());
	anamag_cur.freeze();
	msg.format(FR("Ricostruzione saldi esercizio %04d : azzeramento..."), codes);
	anamag_cur.scan(reset_giac, (void *) &p, msg);

	TString filter; filter << CLIFOGIAC_ANNOES << "==" << p.codesprec;
	TCursor c(new TRelation(LF_CLIFOGIAC), filter);

	msg.format(FR("Ricostruzione saldi esercizio %04d : azzeramento giacenze clienti..."), codes);
	c.scan(rel_reset_clifogiac, (void *) &p, msg);
  
  // ricostruisce i saldi (ciclo sui movimenti)
  bool ok = true;

  TRelation relmovmag(LF_MOVMAG);
	TRectype& rec = relmovmag.curr();
	rec.put(MOVMAG_ANNOES, codes);

	TCursor mov_cur(&relmovmag ,"", 2, &rec, &rec);
  relmovmag.lfile().set_curr(new TMov_mag());
	msg.format(FR("Ricostruzione saldi esercizio %04d : ricalcolo..."), codes);
	mov_cur.scan(recalc_mov, (void *) &ok, msg);

	a.close();
  return ok;
}