// 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 "rmovmag.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() 
{   
}

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());
          //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(TLocalisamfile & mag, 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(TLocalisamfile & clifomag, 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);
  TLocalisamfile clifomag(LF_CLIFOGIAC);
  const TRecord_array& b = body();
	const TString8 hcodcaus(get(MOVMAG_CODCAUS));
	const long numreg = get_long(MOVMAG_NUMREG);

  mag.setkey(2);
  clifomag.setkey(2);
  
  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);
	      
				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()
				 )
		{
			TSaldo_mag & saldo = (TSaldo_mag &)_saldi_mag[*curr_key];
			TArticolo_giacenza & art = cached_article_balances(saldo.codart());
	    
			if (art.lock_and_prompt(lock ? _testandlock : _nolock))
			{
				giac_putkey(mag, saldo);
				if (mag.read() != NOERR)
				{
					giac_putkey(mag, saldo);
					mag.put(MAG_NRIGA, art.mag(get(MOVMAG_ANNOES)).rows() + 1);
					mag.write();
				}
				update_balances(mag.curr(), saldo);
				mag.rewrite();

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

	if (_saldi_mag_clifo.items() > 0)
	{
		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, saldo);
				if (clifomag.read() != NOERR)
				{
					// non trovato: aggiungo
					clifomag.setkey(1);
					giac_putkey_clifo(clifomag, saldo);
					clifomag.put(CLIFOGIAC_NRIGA, 999);
					if (clifomag.read(_isgteq) == NOERR)
						clifomag.prev();
					int nriga = 1;
					if (clifomag.get_int(CLIFOGIAC_ANNOES) == saldo.codes() &&
							clifomag.get_char(CLIFOGIAC_TIPOCF) == saldo.tipocf() &&
							clifomag.get(CLIFOGIAC_CODCF) == saldo.codcf() &&
							clifomag.get(CLIFOGIAC_CODART) == saldo.codart() &&
							clifomag.get(CLIFOGIAC_LIVELLO ) == saldo.livello())
						nriga = clifomag.get_int(CLIFOGIAC_NRIGA) + 1;
					giac_putkey_clifo(clifomag, saldo);
					clifomag.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 * (real)caus.sgn(s_giac)); // update ..
    update_balance(magrec, MAG_ACQ, diff * (real)caus.sgn(s_acq)); // update ..
    update_balance(magrec, MAG_ENT, diff * (real)caus.sgn(s_ent));
    update_balance(magrec, MAG_VEN, diff * (real)caus.sgn(s_ven));
    update_balance(magrec, MAG_USC, diff * (real)caus.sgn(s_usc));
    update_balance(magrec, MAG_ORDC, diff * (real)caus.sgn(s_ordc));
    update_balance(magrec, MAG_ORDF, diff * (real)caus.sgn(s_ordf));
    update_balance(magrec, MAG_RIM, diff * (real)caus.sgn(s_rim));
    update_balance(magrec, MAG_SCARTI, diff * (real)caus.sgn(s_scart));
    update_balance(magrec, MAG_INCL, diff * (real)caus.sgn(s_incl));
    update_balance(magrec, MAG_ACL, diff * (real)caus.sgn(s_acl));
    update_balance(magrec, MAG_PRODCOMP, diff * (real)caus.sgn(s_prodc));
    update_balance(magrec, MAG_PRODFIN, diff * (real)caus.sgn(s_prodf));
    update_balance(magrec, MAG_NLABEL, diff * (real)caus.sgn(s_label));
    update_balance(magrec, MAG_USER1, diff * (real)caus.sgn(s_user1));
    update_balance(magrec, MAG_USER2, diff * (real)caus.sgn(s_user2));
    update_balance(magrec, MAG_USER3, diff * (real)caus.sgn(s_user3));
    update_balance(magrec, MAG_USER4, diff * (real)caus.sgn(s_user4));
    update_balance(magrec, MAG_USER5, diff * (real)caus.sgn(s_user5));
    update_balance(magrec, MAG_USER6, diff * (real)caus.sgn(s_user6));
  }
  if (caus.update_val())
  {
		const real diff_val = s.valore();

    update_balance(magrec, MAG_VALACQ, diff_val * (real)caus.sgn(s_acq)); // update ..
    update_balance(magrec, MAG_VALENT, diff_val * (real)caus.sgn(s_ent));
    update_balance(magrec, MAG_VALVEN, diff_val * (real)caus.sgn(s_ven));
    update_balance(magrec, MAG_VALUSC, diff_val * (real)caus.sgn(s_usc));
    update_balance(magrec, MAG_VALORDC, diff_val * (real)caus.sgn(s_ordc));
    update_balance(magrec, MAG_VALORDF, diff_val * (real)caus.sgn(s_ordf));
    update_balance(magrec, MAG_VALRIM, diff_val* (real)caus.sgn(s_rim));
    update_balance(magrec, MAG_VALSCARTI, diff_val * (real)caus.sgn(s_scart));
    update_balance(magrec, MAG_USERVAL1, diff_val * (real)caus.sgn(s_user1));
    update_balance(magrec, MAG_USERVAL2, diff_val * (real)caus.sgn(s_user2));
    update_balance(magrec, MAG_USERVAL3, diff_val * (real)caus.sgn(s_user3));
    update_balance(magrec, MAG_USERVAL4, diff_val * (real)caus.sgn(s_user4));
    update_balance(magrec, MAG_USERVAL5, diff_val * (real)caus.sgn(s_user5));
    update_balance(magrec, MAG_USERVAL6, diff_val * (real)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 * (real)caus.sgn(s_giac)); // update ..
    update_balance(clifomagrec, CLIFOGIAC_ACQ, diff * (real)caus.sgn(s_ven)); // update ..
    update_balance(clifomagrec, CLIFOGIAC_ENT, diff * (real)caus.sgn(s_usc));
    update_balance(clifomagrec, CLIFOGIAC_VEN, diff * (real)caus.sgn(s_acq));
    update_balance(clifomagrec, CLIFOGIAC_USC, diff * (real)caus.sgn(s_ent));
    update_balance(clifomagrec, CLIFOGIAC_ORDC, diff * (real)caus.sgn(s_ordf));
    update_balance(clifomagrec, CLIFOGIAC_ORDF, diff * (real)caus.sgn(s_ordc));
    update_balance(clifomagrec, CLIFOGIAC_RIM, -diff * (real)caus.sgn(s_rim));
    update_balance(clifomagrec, CLIFOGIAC_SCARTI, -diff * (real)caus.sgn(s_scart));
    update_balance(clifomagrec, CLIFOGIAC_INCL, diff * (real)caus.sgn(s_acl));
    update_balance(clifomagrec, CLIFOGIAC_ACL, diff * (real)caus.sgn(s_incl));
    update_balance(clifomagrec, CLIFOGIAC_PRODCOMP, -diff * (real)caus.sgn(s_prodc));
    update_balance(clifomagrec, CLIFOGIAC_PRODFIN, -diff * (real)caus.sgn(s_prodf));
    update_balance(clifomagrec, CLIFOGIAC_DOTIN, diff * (real)caus.sgn(s_dotin));
    update_balance(clifomagrec, CLIFOGIAC_DOTOD, diff * (real)caus.sgn(s_dotod));
    update_balance(clifomagrec, CLIFOGIAC_DOTTM, diff * (real)caus.sgn(s_dottm));
    update_balance(clifomagrec, CLIFOGIAC_USER1, diff * (real)caus.sgn(s_user1));
    update_balance(clifomagrec, CLIFOGIAC_USER2, diff * (real)caus.sgn(s_user2));
    update_balance(clifomagrec, CLIFOGIAC_USER3, diff * (real)caus.sgn(s_user3));
    update_balance(clifomagrec, CLIFOGIAC_USER4, diff * (real)caus.sgn(s_user4));
    update_balance(clifomagrec, CLIFOGIAC_USER5, diff * (real)caus.sgn(s_user5));
    update_balance(clifomagrec, CLIFOGIAC_USER6, diff * (real)caus.sgn(s_user6));
  }
  if (caus.update_val())
  {
		const real diff_val = s.valore();

    update_balance(clifomagrec, CLIFOGIAC_VALACQ, diff_val * (real)caus.sgn(s_ven)); // update ..
    update_balance(clifomagrec, CLIFOGIAC_VALENT, diff_val * (real)caus.sgn(s_usc));
    update_balance(clifomagrec, CLIFOGIAC_VALVEN, diff_val * (real)caus.sgn(s_acq));
    update_balance(clifomagrec, CLIFOGIAC_VALUSC, diff_val * (real)caus.sgn(s_ven));
    update_balance(clifomagrec, CLIFOGIAC_VALORDC, diff_val * (real)caus.sgn(s_ordf));
    update_balance(clifomagrec, CLIFOGIAC_VALORDF, diff_val * (real)caus.sgn(s_ordc));
    update_balance(clifomagrec, CLIFOGIAC_VALRIM, -diff_val* (real)caus.sgn(s_rim));
    update_balance(clifomagrec, CLIFOGIAC_VALSCARTI, -diff_val * (real)caus.sgn(s_scart));
    update_balance(clifomagrec, CLIFOGIAC_USERVAL1, diff_val * (real)caus.sgn(s_user1));
    update_balance(clifomagrec, CLIFOGIAC_USERVAL2, diff_val * (real)caus.sgn(s_user2));
    update_balance(clifomagrec, CLIFOGIAC_USERVAL3, diff_val * (real)caus.sgn(s_user3));
    update_balance(clifomagrec, CLIFOGIAC_USERVAL4, diff_val * (real)caus.sgn(s_user4));
    update_balance(clifomagrec, CLIFOGIAC_USERVAL5, diff_val * (real)caus.sgn(s_user5));
    update_balance(clifomagrec, CLIFOGIAC_USERVAL6, diff_val * (real)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, real diff) const
{
  rec.put(fieldname,rec.get_real(fieldname) + diff);
}

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

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);
  else
    articolo.riporta_saldi(p.codesprec, p.codes, p.tipov, p.catv, p.codl);
	return true;
}

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

	TRectype oldrec(rec);

	oldrec.put(CLIFOGIAC_ANNOES, p.codesprec);
	if (oldrec.read(rel.lfile()) == NOERR)
	{
	  const real acq = rec.get_real(MAG_ACQ) +  rec.get_real(MAG_RIM);
		const real valacq = rec.get_real(MAG_VALACQ) + rec.get_real(MAG_VALRIM);                       
		real val = acq.is_zero() ? ZERO : valacq / acq;
    const TPrice p(val);
    real rim;
		
		rim += rec.get_real(MAG_GIAC); 
    rim += rec.get_real(MAG_PRODFIN);
    rim -= rec.get_real(MAG_PRODCOMP);
    rim += rec.get_real(MAG_ACL);
    rim -= rec.get_real(MAG_INCL);

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

    rec.put(MAG_RIM, rim); rec.put(MAG_VALRIM, c.get_num());
    rec.zero(MAG_ACQ); rec.zero(MAG_VALACQ);
    rec.zero(MAG_ENT); rec.zero(MAG_VALENT);
    rec.zero(MAG_VEN); rec.zero(MAG_VALVEN);
    rec.zero(MAG_USC); rec.zero(MAG_VALUSC);
    rec.zero(MAG_SCARTI);
    if (!riporta_ordinato())
    {
      rec.zero(MAG_ORDF); rec.zero(MAG_VALORDF);
      rec.zero(MAG_ORDC); rec.zero(MAG_VALORDC);
    }
  }
	else
	{
    for (int i = 0; zero_fields[i]; i++)
      rec.zero(zero_fields[i]);
	}
	return rec.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.catv = catven;
	p.codl = codlis;
	
  // 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 << "==" << codes;
	TCursor c(new TRelation(LF_CLIFOGIAC), filter);

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

	rec.put(MOVMAG_ANNOES, codes);

	TCursor mov_cur(new TRelation(LF_MOVMAG),"", 2, &rec, &rec);

  mov_cur.relation()->lfile().set_curr(new TMov_mag());
	mov_cur.freeze();
	msg.format(FR("Ricostruzione saldi esercizio %04d : ricalcolo..."), codes);
	mov_cur.scan(recalc_mov, (void *) &ok, msg);

	a.close();
  return ok;
}