#include <currency.h>
#include <prefix.h>
#include <tabutil.h>

#include "velib.h"
#include "sconti.h"             
#include "vepriv.h"
#include "verig.h"
#include "../mg/mglib.h"

#include "rdoc.h"             

// TArticolo_cache
///////////////////////////////////////////////////////////
class TCache_articoli : public TRecord_cache
{
protected:
  virtual TObject* rec2obj(const TRectype& rec) const;

public:
  TArticolo_giacenza& art(const char* key);
  TCache_articoli();
  virtual ~TCache_articoli() { }
};

TCache_articoli::TCache_articoli()
               : TRecord_cache(LF_ANAMAG, 1)
{ 
  test_file_changes();   // Tieni d'occhio le modifiche sul file
  set_items_limit(256);  // Standard
}        

TObject* TCache_articoli::rec2obj(const TRectype& curr) const
{
  return new TArticolo_giacenza(curr);
}

TArticolo_giacenza & TCache_articoli::art(const char* key)
{
  TArticolo_giacenza& art = (TArticolo_giacenza&)query(key);
  return art;
}

///////////////////////////////////////////////////////////
// Tipo riga di un documento
///////////////////////////////////////////////////////////

TAssoc_array TTipo_riga_documento::_formule_riga;

TTipo_riga_documento::TTipo_riga_documento(const char* tiporig)
                    : TRectype(LF_TABCOM)
{
  settab("TRI");
  _name = "verig";
  _name << codice();
  if (tiporig && *tiporig)
    read(tiporig);
}

TTipo_riga_documento::TTipo_riga_documento(const TRectype& rec)
                    : TRectype(rec)
{
  _name = "verig";
  _name << codice();
  read_formule();
}

TTipo_riga_documento::~TTipo_riga_documento()
{
}

int TTipo_riga_documento::read(const char* tiporig)
{                                   
  TTable t("%TRI");
  put("CODTAB", tiporig);
  int err = TRectype::read(t);
  if (err == NOERR)
    read_formule();
  else
    yesnofatal_box("Tipo riga documento errato: %s", tiporig);
  return err;  
}

void TTipo_riga_documento::read_formule()

{               
  TFilename prof(profile_name());
  
  prof.ext("ini");    
  
  TConfig profile(prof);
  
  _formule = profile.get("CAMPICALC", "MAIN");
  const TString & calcoli = profile.get("CALCOLI", "MAIN");

  if (calcoli == "*")
  {            
    TTable frd("%FRR");
    
    for (int err = frd.first(); err == NOERR; err = frd.next())
    {
      const TString & formula = frd.get("CODTAB");
      if (_formule.find(formula) < 0)
        _formule.add(formula);
    }
  }     
  else
    _formule.add(calcoli);
  _imponibile = profile.get("IMPONIBILE", "MAIN");
  if (_imponibile.empty())
    _imponibile = "IMPONIBILE";
  
  TTable frr("%FRR");
  
  frr.put("CODTAB", _imponibile);     
  if (frr.read() != NOERR)
    _formule_riga.add(_imponibile, new TFormula_documento(_riga, _imponibile, "IMPORTO(1)"), TRUE);
  if (_formule.find(_imponibile) < 0)
    _formule.add(_imponibile);

  _provv = profile.get("PROVV", "MAIN");

  _qtares = profile.get("QTARES", "MAIN");
  if (_qtares.empty())
    _qtares = "QTARES";

  frr.put("CODTAB", _qtares);     
  if (frr.read() != NOERR)
    _formule_riga.add(_qtares, new TFormula_documento(_riga, _qtares, "QTARES()"), TRUE);
  if (_formule.find(_qtares) < 0)
    _formule.add(_qtares);

  _rigavalres = profile.get("RIGAVALRES", "MAIN");
  if (_rigavalres.empty())
    _rigavalres = "RIGAVALRES";

  _rigavalore = profile.get("RIGAVALORE", "MAIN");
  if (_rigavalore.empty())
    _rigavalore = "RIGAVALORE";
                                                   
  frr.put("CODTAB", _rigavalres);
  if (frr.read() != NOERR)
    _formule_riga.add(_rigavalres, new TFormula_documento(_riga, _rigavalres, "QTARES()*PREZZO(1,0)"), TRUE);
  if (_formule.find(_rigavalres) < 0)
    _formule.add(_rigavalres);

  frr.put("CODTAB", _rigavalore);
  if (frr.read() != NOERR)
    _formule_riga.add(_rigavalore, new TFormula_documento(_riga, _rigavalore, "QTA*PREZZO(1,0)"), TRUE);
  if (_formule.find(_rigavalore) < 0)
    _formule.add(_rigavalore);  
}

TFormula_documento * TTipo_riga_documento::succ_formula(bool restart)
{                                 
  if (restart)
    _formule.restart();
  const TString formula(_formule.get());
  if (formula.not_empty())
  {
    char *expr = NULL;
    const int p = formula.find("=");
    if (p > 0)
    {
      expr = (char *) (const char *) formula + p;
      *expr = '\0'; expr++;
    }  
    TFormula_documento * o = (TFormula_documento*)_formule_riga.objptr(formula);
    if (o == NULL)
    {
      o = new TFormula_documento(_riga, formula, expr);
      _formule_riga.add(formula, o);
    }
    return o;
  }
  else
    return NULL;
}  

///////////////////////////////////////////////////////////
// Riga documento per vendite
///////////////////////////////////////////////////////////

long TRiga_documento::_firm = -1;
TAssoc_array TRiga_documento::_tipi;
TAssoc_array TRiga_documento::_spese;
TAssoc_array TRiga_documento::_ive;
TCache_articoli * TRiga_documento::_articoli = NULL;

TRiga_documento::TRiga_documento(TDocumento* doc, const char * tipo)
               : TAuto_variable_rectype(LF_RIGHEDOC), _doc(doc), _iva_calc(FALSE)
{
  if (tipo)
    set_tipo(tipo);
}

TRiga_documento::TRiga_documento(const TRiga_documento & row)
               : TAuto_variable_rectype(LF_RIGHEDOC), _doc(NULL), _iva_calc(FALSE)

{       
  copy(row);
}

TRiga_documento::TRiga_documento(const TRiga_documento& rec, TDocumento* doc,
                                 const char * tipo)
               : TAuto_variable_rectype(rec), _doc(doc), _iva_calc(FALSE)
{
  if (tipo)
    set_tipo(tipo);
}
                  
const TTipo_riga_documento& TRiga_documento::tipo() const
{
  const TString16 tiporig(get(RDOC_TIPORIGA));
  CHECK(tiporig.not_empty(), "Tipo riga documento nullo");
  TTipo_riga_documento* o = (TTipo_riga_documento*)_tipi.objptr(tiporig);
  if (o == NULL)
  {
    if (_tipi.items() == 0)
    {
      TTable tri("%TRI");  // Tabella dei tipi riga
      for (tri.first(); !tri.eof(); tri.next())
      {
        const TString16 codice = tri.get("CODTAB");
        _tipi.add(codice, new TTipo_riga_documento(tri.curr()));
      }  
    }  
    o = (TTipo_riga_documento*)_tipi.objptr(tiporig);
    if (o == NULL)
    {
      o = new TTipo_riga_documento(tiporig);
      _tipi.add(tiporig, o);
    }
  }
  return *o;
}  

const TSpesa_prest & TRiga_documento::spesa() const
{                           
  const char tipor = tipo().tipo();
  
  CHECK(tipor == RIGA_SPESEDOC || tipor == RIGA_PRESTAZIONI, "Tipo riga incompatibile con le spese");

  test_firm();
          
  const TString16 codice(get("CODART"));
  TString16 index; index << tipor << codice;
    
  TSpesa_prest * s = (TSpesa_prest *) _spese.objptr(index);
  if (s == NULL)
  {
    s = new TSpesa_prest(codice, tipor);
    _spese.add(index, s);
  }                
  return *s;
}                      

const TCodiceIVA & TRiga_documento::iva(const char *codice)
{
  TCodiceIVA * v = (TCodiceIVA *) _ive.objptr(codice);
  if (v == NULL)
  {
    v = new TCodiceIVA(codice);
    _ive.add(codice, v);
  }                
  return *v;
}                      

bool TRiga_documento::sola_descrizione() const
{       
  char t = tipo().tipo();
  if (t <= ' ' && get("QTA").empty() && get("PREZZO").empty())
    t = RIGA_DESCRIZIONI;
  return t == RIGA_DESCRIZIONI;
}

void TRiga_documento::forza_sola_descrizione() 
{     
  // In realta' il test serve anche a caricare la lista dei tipi riga!
  if (!tipo_valido() || !is_descrizione())
  {                  
    TString16 cod;
    _tipi.restart();
    for (const TObject* o = _tipi.get(); o; o = _tipi.get())
    {
      const TTipo_riga_documento* trd = (const TTipo_riga_documento*)o;
      if (trd->tipo() == RIGA_DESCRIZIONI)
      {       
        const TString& c = trd->codice();
        if (cod.empty() || c < cod)
          cod = c;
      }
    }
    put("TIPORIGA", cod);
    zero("QTA");
    zero("PREZZO");
  }
}
   
TRiga_documento & TRiga_documento::copy(const TRiga_documento & r) 
{ 
  _doc = r._doc;                             
  operator=((TRectype &)r); 
  return *this;
}                               

void TRiga_documento::set_variables(TExpression * e) const
{ 
  const int items = e->numvar();
  for  (int i = 0; i < items; i++)
  {                          
    const TFieldref field(e->varname(i), LF_RIGHEDOC);

    switch (field.file())
    {
      case LF_ANAMAG :                    
        { 
          const TArticolo_giacenza * art = articolo();

          if (art != NULL)
            e->setvar(i, art->get(field.name()));
        }
        break;            
      case LF_DOC   :
        e->setvar(i, doc().get(field.name()));
        break;
      case LF_CLIFO :
        e->setvar(i, doc().clifor().get(field.name()));
        break;
      case LF_CFVEN :
        e->setvar(i, doc().clifor().vendite().get(field.name()));
        break;
      default:
        e->setvar(i, get(field.name()));
        break;
    }   
  }
}  

void TRiga_documento::test_firm()
{                
  const long new_firm = prefix().get_codditta();
  
  if (_firm != new_firm)
  {
    _spese.destroy();
    _firm = new_firm;
  }
}
                    
TRectype & TRiga_documento::operator =(const TRectype & r) 
{
  TRectype::operator=(r); 
  reset_fields(*this);
  set_fields(*this);
  return *this;
}
                                   
TRectype & TRiga_documento::operator =(const char * r) 
{
  TRectype::operator=(r); 
  reset_fields(*this);
  set_fields(*this);
  return *this;
}
                                   
// Ritorna TRUE se le due righe del documento possono essere sommate
bool TRiga_documento::raggruppabile(const TRiga_documento& r, TToken_string& campi) const
{                                    
  bool ok = TRUE;
  TString campo;
  for (const char* c = campi.get(0); c && ok; c = campi.get())
  {
    campo = get(c);            // Separare le due get!
    ok &= campo == r.get(c);
  }  
  return ok;  
}

TRiga_documento& TRiga_documento::operator +=(const TRiga_documento& r)
{
  TToken_string campi("QTA|NCOLLI|QTAEVASA");
  for (const char* c = campi.get(0); c; c = campi.get())
  {
    real num = r.get_real(c);
    if (!num.is_zero())
    {
      num += get_real(c);
      put(c, num);
    }  
  }   
  if (!get_bool("RIGAEVASA"))
  {                                              
    const real qta = get_real("QTA");
    const real qtaeva = get_real("QTAEVASA");
    if (qtaeva >= qta)
      put("RIGAEVASA", "X");
  }
  
  return *this;
}

void TRiga_documento::set_fields(TAuto_variable_rectype & rec)
{ 
  if (get(RDOC_TIPORIGA).not_empty())
  {                                                                 
    TTipo_riga_documento & tipo_riga = (TTipo_riga_documento &) tipo();
    
    for (const TFormula_documento * f = tipo_riga.first_formula(); f; f = tipo_riga.succ_formula())
    {        
      TExpr_documento * exp = f->expr();
      if (exp)
      {
        exp->set_doc(_doc);
        exp->set_row(this);
        add_field(new TDocumento_variable_field(f->name(), *exp));
      }
    }
  }
}

real TRiga_documento::prezzo(bool scontato, bool lordo, int ndec) const
{                          
  if (ndec == AUTO_DECIMALS)
    ndec = doc().decimals(TRUE);
    
  real prezzo = get_real("PREZZO");
  if (doc().tipo().calcolo_lordo())
  {
    if (lordo)                   
      prezzo = iva().lordo(prezzo, ndec);
    prezzo.round(ndec);
    if (scontato)
      prezzo = prezzo_scontato(prezzo, get("SCONTO"));
    prezzo.round(ndec);
  }
  else
  {
    if (scontato)
      prezzo = prezzo_scontato(prezzo, get("SCONTO"));
    prezzo.round(ndec);
    if (lordo)
      prezzo = iva().lordo(prezzo, ndec);
    prezzo.round(ndec);
  }
  return prezzo;
}
                               
real TRiga_documento::importo(bool scontato, bool lordo, int ndec) const
{
  if (ndec == AUTO_DECIMALS)
    ndec = doc().decimals();
    
  real importo;                      
  const bool doc_al_lordo = doc().tipo().calcolo_lordo();
  TTipo_calcolo c = _nessun_calcolo;
  const char tipor = tipo().tipo();
  const real qta = get_real("QTA");         
  real r1;
    
  switch (tipor)
  {
    case RIGA_MERCE:
      c = _qtaprezzo;
      break;
    case RIGA_PRESTAZIONI:
    case RIGA_SPESEDOC:
      {   
        const TSpesa_prest & s = spesa(); 
                             
        switch (s.tipo())
        {
          case 'Q':
            c = _qtaprezzo;
            break;
          case 'V':
            c = _valore;
            break;
          case 'P':                         
            { 
              const TString16 field_perc(s.field_perc());
              
              c = _percentuale;
              r1 = doc().get_real(field_perc);
            }
            break;
          default:
            break;  
        }
      }
      break;
    case RIGA_SCONTI:                   
      {
        TCond_vendita cv(NULL, NULL); 
        cv.set_sconto(get("SCONTO"));
        if (cv.get_sconto().not_empty())
          c = _scontoperc;
        else
          c = _scontoimp;             
        r1 = cv.sconto_val();
      }
      break;
    case RIGA_OMAGGI: 
      if (_iva_calc && get_bool("ADDIVA"))
        c = _qtaprezzo;
    default:
      break;
  }
  switch (c)     
  {
    case _qtaprezzo:  
      if (doc_al_lordo)
        importo = prezzo(scontato, TRUE, ndec) * qta;
      else  
        importo = prezzo(scontato, lordo, ALL_DECIMALS) * qta;
      break;
    case _valore:
      importo = prezzo(scontato, doc_al_lordo ? TRUE : lordo, ndec);
      break;
    case _percentuale: 
        importo = r1 * get_real(RDOC_QTA) / 100;
      break;
    case _scontoimp:
      importo = -prezzo(FALSE, doc_al_lordo ? TRUE : lordo, ndec);
      break;
    case _scontoperc:     
      importo = doc().basesconto() * (r1 - 1.0);
     break;                  
    default:
      break;
  }
  importo.round(ndec);                                     
  
  if (doc_al_lordo && !lordo)
    iva().scorpora(importo, ndec);
    
  return importo;
}       
  
real TRiga_documento::iva(int ndec) const
{  
  if (ndec == AUTO_DECIMALS)
    ndec = doc().decimals();
              
  ((TRiga_documento *) this)->_iva_calc = TRUE;
  const real zanicchi = is_sconto() ? ZERO : iva().imposta(imponibile(), ndec);
  ((TRiga_documento *) this)->_iva_calc = FALSE;
  return zanicchi;
}

real TRiga_documento::imponibile_omaggio() const 
{
  ((TRiga_documento *) this)->_iva_calc = TRUE;
  const real  imp = imponibile();
  ((TRiga_documento *) this)->_iva_calc = FALSE;
  return imp;
}

real TRiga_documento::imponibile(bool lordo) const 
{              
  const TString& field = tipo().imponibile();
  if (field.not_empty())  
  {           
    real r;
    if (is_omaggio() && _iva_calc)
    {        
      TDocumento_variable_field * f = (TDocumento_variable_field *) variable_field(field);
      CHECKS(f, "Field UNKNOWN : ", field);
      f->set_dirty(TRUE);
      r = get_real(field);
      f->set_dirty(TRUE);
    }
    else
      r = get_real(field);
    if (lordo)
      r = iva().lordo(r, doc().decimals(TRUE));
    return r;
  }
  return importo(TRUE, lordo, doc().decimals());
}

real TRiga_documento::imposta(bool round) const
{ 
  return iva(round ? doc().decimals() : 20);
}

real TRiga_documento::provvigione(int ndec) const
{
  real val;
  
  if (ndec == AUTO_DECIMALS)
    ndec = doc().decimals();
  const TString & field = tipo().provv();
  if (field.not_empty())
    val = get_real(field);
  else 
  {
    const char tipo_riga = tipo().tipo();
    val = importo(TRUE, FALSE, ndec);
    
    if (tipo_riga == RIGA_MERCE || tipo_riga == RIGA_SPESEDOC || tipo_riga == RIGA_PRESTAZIONI)
    {
      real netto_testa;                                             
      TString80 s;
      
      if (scontoexpr2perc(doc().get(DOC_SCONTOPERC), FALSE, s, netto_testa) && netto_testa != ZERO)
        val *= netto_testa;
    } 
    
    val *= get_real(RDOC_PERCPROV) / 100.0; 
  }
  val.round(ndec);
  return val;
}

real TRiga_documento::qtaresidua() const
{
  real val;
  
  if (!get_bool(RDOC_RIGAEVASA))
  {
    val = get_real(RDOC_QTA) - get_real(RDOC_QTAEVASA);
    if (val < ZERO)
      val = ZERO;
  }
  return val;
}

real TRiga_documento::valore(bool totale, int ndec) const
{
  real val;

  if (ndec == AUTO_DECIMALS)
    ndec = doc().decimals();
  
  const TString16 field(totale ? tipo().rigavalore() : tipo().rigavalres());

  if (field.not_empty())
    val = get_real(field);
  val.round(ndec);
  return val;
}

void TRiga_documento::dirty_fields(bool dirty_document)
{
  for (TDocumento_variable_field * f = (TDocumento_variable_field *) first_variable_field(); 
    f != NULL; f = (TDocumento_variable_field *) succ_variable_field()) 
    f->set_dirty();
  if (dirty_document) 
    ((TDocumento &)doc()).dirty_fields();  
}

bool TRiga_documento::doc_dependent() const 

{                     
  if (tipo_valido())
  {
    const char tipor = tipo().tipo();
               
    if (tipor == RIGA_SPESEDOC)
      return spesa().tipo() == 'P';
    else
      if (tipor == RIGA_SCONTI)
        return get("SCONTO").not_empty();
  }                 
  return FALSE;
}

void TRiga_documento::put_str(const char* fieldname, const char* val)
{          
  if (strcmp(fieldname, RDOC_TIPORIGA) == 0)
  {
    const TString16 v(val);
    if (TRectype::get(RDOC_TIPORIGA) != v)
    {
      TAuto_variable_rectype::put_str(fieldname, v);
      reset_fields(*this);
      set_fields(*this);
    }
    else
      dirty_fields();
  }
  else
  {
    TAuto_variable_rectype::put_str(fieldname, val);
    dirty_fields();
  }
}                                         

bool TRiga_documento::is_articolo() const
{
  const char t = tipo().tipo();
  return (t == RIGA_MERCE || t == RIGA_OMAGGI) && get("CODARTMAG").not_empty();
}                                                                     

void TRiga_documento::zero(const char * fieldname)
{
  if (strcmp(fieldname, RDOC_TIPORIGA) == 0)
    reset_fields(*this);
  TAuto_variable_rectype::zero(fieldname);
  dirty_fields();
}

void TRiga_documento::zero(char c)
{
  reset_fields(*this);
  TAuto_variable_rectype::zero(c);
}

void TRiga_documento::autosave(TSheet_field & f)
{
  const int num = numero() - 1;
  
  if (num >= 0 && num < f.items())
  { 
    TToken_string & row = f.row(num);
    
    const int lordo_id = f.cid2index(FR_LORDO);
    const bool lordo =  strcmp(row.get(lordo_id), "X") == 0;      
    if (lordo)
    {
      row.add(" ", lordo_id);
      f.sheet_mask().reset(FR_LORDO);
    }
    put( "TIPORIGA", row.get( f.cid2index(FR_TIPORIGA )) );
    TString16 codmag(row.get(f.cid2index(FR_CODMAG)));
      
    codmag.left_just(3);
    codmag << row.get( f.cid2index(FR_CODDEP ));
    put( "CODMAG",  codmag);
    put( "CODART", row.get( f.cid2index(FR_CODART )) );   
    TString liv;
    for (int l = 0; l<4 ; l++)
      doc().livelli().pack_grpcode(liv,row.get(f.cid2index(FR_LIV1+l)),l+1);
    put( "LIVELLO", liv); // da modificare
    TString s(row.get(f.cid2index(FR_DESCR))); 
    int split_pos = s.find('\n');
                                         
    const int descr_len = length("DESCR");
    if (split_pos < 0 && s.len() > descr_len)
      split_pos = descr_len;
    if (split_pos > descr_len)
      split_pos = descr_len;
    if (split_pos > 0)
    {
      put( "DESCR", s.left(split_pos));
      put("DESCLUNGA", "X");  
      put("DESCEST", s.mid(split_pos));
    }
    else
    {
      put("DESCR", s);
      put("DESCLUNGA", "");
      zero("DESCEST");
    }
    
    const int prezzo_id = f.cid2index(FR_PREZZO);
    real prezzo(row.get(prezzo_id));
    const TString16 codiva(row.get(f.cid2index(FR_CODIVA)));
    if (lordo)                   
    { 
      iva(codiva).scorpora(prezzo, doc().decimals(TRUE));

      const TString prezzo_str(prezzo.string());

      row.add(prezzo_str, prezzo_id);
      f.sheet_mask().set(FR_PREZZO, prezzo_str);
    }
    put( "PREZZO", prezzo);
    put( "UMQTA", row.get( f.cid2index(FR_UMQTA )) );
    put( "QTA", row.get( f.cid2index(FR_QTA )) );
    put( "QTAEVASA", row.get( f.cid2index(FR_QTAEVASA )) );
    put( "RIGAEVASA", row.get( f.cid2index(FR_RIGAEVASA )) );
    put( "TARA", row.get( f.cid2index(FR_TARA )) );
    put( "PNETTO", row.get( f.cid2index(FR_PNETTO )) );
    put( "NCOLLI", row.get( f.cid2index(FR_NCOLLI )) );
    put( "DAEVADERE", row.get( f.cid2index(FR_DAEVADERE )) );  
    put( "SCONTO", row.get( f.cid2index(FR_SCONTO )) );
    put( "PERCPROV", row.get( f.cid2index(FR_PERCPROV )) );
    put( "IMPFISUN", row.get( f.cid2index(FR_IMPFISUN )) );
    put( "IMPFISSO", row.get( f.cid2index(FR_IMPFISSO )) );
    put( "CODIVA", codiva);
    put( "ADDIVA", row.get( f.cid2index(FR_ADDIVA )) );
    put( "ASPBENI", row.get( f.cid2index(FR_ASPBENI )) );  
    put( "CAUSMAG", row.get( f.cid2index(FR_CAUS )) );  
    TString16 codmagc(row.get(f.cid2index(FR_CODMAGC)));
      
    codmagc.left_just(3);
    codmagc << row.get( f.cid2index(FR_CODDEPC ));
    put( "CODMAGC",  codmagc);
    put( "DATACONS", row.get( f.cid2index(FR_DATACONS )) );  
    put( "CODARTMAG", row.get( f.cid2index(FR_CODARTMAG )) );  
    put( "CHECKED", row.get( f.cid2index(FR_CHECKED )) );  
    put( RDOC_QTAGG1, row.get( f.cid2index(FR_QTAGG1 )) );  
    put( RDOC_QTAGG2, row.get( f.cid2index(FR_QTAGG2 )) );  
    put( RDOC_QTAGG3, row.get( f.cid2index(FR_QTAGG3 )) );  
    put( RDOC_QTAGG4, row.get( f.cid2index(FR_QTAGG4 )) );  
    put( RDOC_QTAGG5, row.get( f.cid2index(FR_QTAGG5 )) );  
    put( RDOC_IMPIANTO, row.get( f.cid2index(FR_IMPIANTO )) );  
    put( RDOC_LINEA, row.get( f.cid2index(FR_LINEA )) );  
  }
}                 
    
void TRiga_documento::autoload(TSheet_field & f)
{ 
  const int num = numero() - 1;
  
  TToken_string & row = f.row(num);
  row.cut(0);
    
  row.add( get( "TIPORIGA" ), f.cid2index(FR_TIPORIGA ));
  const TString16 codmag(get("CODMAG"));
  row.add( codmag.left(3), f.cid2index(FR_CODMAG ));
  row.add( codmag.mid(3), f.cid2index(FR_CODDEP ));
  row.add( get( "CODART" ), f.cid2index(FR_CODART ));
  TString16 liv(get("LIVELLO"));

  for (int l = 0; l<4 ; l++)
    row.add(doc().livelli().unpack_grpcode(liv,l+1), f.cid2index(FR_LIV1+l ));
  TString s(1024); s = get("DESCR");
  if (get_bool("DESCLUNGA"))
    s << get("DESCEST");
  row.add(s, f.cid2index(FR_DESCR ));
  row.add( get( "UMQTA" ), f.cid2index(FR_UMQTA ));
  row.add( get( "PREZZO" ), f.cid2index(FR_PREZZO ));
  row.add( get( "QTA" ), f.cid2index(FR_QTA ));
  row.add( get( "QTAEVASA" ), f.cid2index(FR_QTAEVASA ));
  row.add( get( "RIGAEVASA" ), f.cid2index(FR_RIGAEVASA ));
  row.add( get( "TARA" ), f.cid2index(FR_TARA ));
  row.add( get( "PNETTO" ), f.cid2index(FR_PNETTO ));
  row.add( get( "NCOLLI" ), f.cid2index(FR_NCOLLI ));
  row.add( get( "DAEVADERE" ), f.cid2index(FR_DAEVADERE ));
  row.add( get( "SCONTO" ), f.cid2index(FR_SCONTO ));
  row.add( get( "PERCPROV" ), f.cid2index(FR_PERCPROV ));
  row.add( get( "IMPFISUN" ), f.cid2index(FR_IMPFISUN  ));
  row.add( get( "IMPFISSO" ), f.cid2index(FR_IMPFISSO ));
  row.add( get( "CODIVA" ), f.cid2index(FR_CODIVA ));
  row.add( get( "ADDIVA" ), f.cid2index(FR_ADDIVA ));
  row.add( get( "ASPBENI" ), f.cid2index(FR_ASPBENI ));
  row.add( get( "CAUSMAG" ), f.cid2index(FR_CAUS ));
  const TString16 codmagc(get("CODMAGC"));
  row.add( codmagc.left(3), f.cid2index(FR_CODMAGC ));
  row.add( codmagc.mid(3), f.cid2index(FR_CODDEPC ));
  row.add( get("DATACONS"), f.cid2index(FR_DATACONS ));
  row.add( get( "CODARTMAG" ), f.cid2index(FR_CODARTMAG));
  row.add( get( "CHECKED" ), f.cid2index(FR_CHECKED));
  row.add( get( RDOC_QTAGG1), f.cid2index(FR_QTAGG1));
  row.add( get( RDOC_QTAGG2), f.cid2index(FR_QTAGG2));
  row.add( get( RDOC_QTAGG3), f.cid2index(FR_QTAGG3));
  row.add( get( RDOC_QTAGG4) , f.cid2index(FR_QTAGG4));
  row.add( get( RDOC_QTAGG5) , f.cid2index(FR_QTAGG5));
  row.add( get( RDOC_IMPIANTO) , f.cid2index(FR_IMPIANTO));
  row.add( get( RDOC_LINEA) , f.cid2index(FR_LINEA));
}

TArticolo_giacenza * TRiga_documento::articolo() const
{                
  if (_articoli == NULL)
    _articoli = new TCache_articoli();

  const TString & codart = get(RDOC_CODARTMAG);
  if (codart.empty())
    return NULL;
  return &(_articoli->art(codart));     
}

void TRiga_documento::set_original_rdoc_key(const TRiga_documento& orig)
{
  put(RDOC_DACODNUM, orig.get(RDOC_CODNUM));
  put(RDOC_DAANNO,   orig.get(RDOC_ANNO));
  put(RDOC_DAPROVV,  orig.get(RDOC_PROVV));
  put(RDOC_DANDOC,   orig.get(RDOC_NDOC));
  put(RDOC_DAIDRIGA, orig.get(RDOC_IDRIGA));
}

void TRiga_documento::reset_original_rdoc_key()
{
  zero(RDOC_DACODNUM);
  zero(RDOC_DAANNO);
  zero(RDOC_DAPROVV);
  zero(RDOC_DANDOC);
  zero(RDOC_DAIDRIGA);
}

const TRectype* TRiga_documento::find_original_rdoc() const
{
  const long id = get_long(RDOC_DAIDRIGA);
  if (id > 0L)
  {
    TToken_string key;
    key.add(get(RDOC_DACODNUM));
    key.add(get(RDOC_DAANNO));
    key.add(get(RDOC_DAPROVV));
    key.add(get(RDOC_DANDOC));
    for (int r = 1; ; r++)
    {
      key.add(r, 4);
      const TRectype& rec = cache().get(LF_RIGHEDOC, key);
      if (rec.empty()) break;
      if (rec.get_long(RDOC_IDRIGA) == id)
        return &rec;
    }
  }
  return NULL;
}