#ifndef __VELIB01_H 
#define __VELIB01_H

#ifndef __RELATION_H
#include <relation.h>
#endif

#ifndef __VARREC_H
#include <varrec.h>
#endif

#ifndef __VARMASK_H
#include <varmask.h>
#endif

#ifndef __PAGAMENT_H
#include "../cg/pagament.h"
#endif

class TDocumento;
class TRiga_documento;
class TCond_vendita;
class TIVA;

real lordo2netto(real& lordo, const TString& codiva, int ndec);
inline real lordo2netto(real& lordo, const TString& codiva, bool is_valuta = FALSE) { return lordo2netto(lordo, codiva, is_valuta ? 3 : 0);}
real netto2lordo(const real& netto, const TString& codiva, int ndec);
inline real netto2lordo(const real& netto, const TString& codiva, bool is_valuta = FALSE) { return netto2lordo(netto, codiva, is_valuta ? 3 : 0);}
real lordo2netto(real& lordo, const real& iva, int ndec);
inline real lordo2netto(real& lordo, const real& iva, bool is_valuta = FALSE) {return lordo2netto(lordo, iva, is_valuta ? 3 : 0); }
real netto2lordo(const real& netto, const real& iva, int ndec);
inline real netto2lordo(const real& netto, const real& iva, bool is_valuta = FALSE) { return netto2lordo(netto, iva, is_valuta ? 3 : 0); }
real prezzo_scontato(const real& prezzo, const char * sconto);
real iva(real imponibile, const TIVA & codiva,int ndec);   

bool ora_hndl(TMask_field& field, KEY key);
bool codcli_hndl(TMask_field& field, KEY key);
bool dummy_hndl(TMask_field& field, KEY key);
bool condpag_hndl(TMask_field& field, KEY key);

class TDocumento_variable_field : public TVariable_field
{                                           
  bool _dirty;
  
  public:
// @cmember segnala che il campo deve essere ricalcolato
  virtual bool dirty() const { return _dirty;}
// @cmember assegna lo stato di campo da ricalcolare
  virtual void set_dirty(bool on = TRUE) { _dirty = on;}

// @ cmember Costruttore con una espressione di calcolo  
  TDocumento_variable_field(const char * name, const char * expr = "", TTypeexp type = _strexpr)
                          : TVariable_field(name, expr, type), _dirty(TRUE) {}
// @ cmember Costruttore con una funzione
  TDocumento_variable_field(const char * name, VIRTUAL_GET_FUNCTION getfunc)
                          : TVariable_field(name, getfunc), _dirty(TRUE) {}
// @ cmember Costruttore con una espressione di calcolo  
  TDocumento_variable_field(const char * name, TExpression * expr, TTypeexp type = _strexpr)
                          : TVariable_field(name, expr, type), _dirty(TRUE) {}
// @ cmember Costruttore con un variable_field 
  TDocumento_variable_field(const TVariable_field & f) : TVariable_field(f), _dirty(TRUE) {}
// @ cmember Distruttore  
  ~TDocumento_variable_field() {}
};

class TSpesa_prest : public TRectype
{                    

protected:
  int read(const char* codice);

public:
  TObject* dup() const { return new TSpesa_prest(codice()); }

public:       
  const TString& codice() const { return get("CODTAB");}  
  const TString& descrizione() const { return get("S0"); } 
  const TString& field_perc() const { return get("S5"); } 
  char tipo() const { return get_char("S6"); }
  char genere() const { return get("COD") == "SPP" ? 'S' : 'P'; }

  TSpesa_prest(const char* codice = NULL, char tipo = 'S');
  TSpesa_prest(const TRectype& rec);
  virtual ~TSpesa_prest() {}
};

class TIVA : public TRectype
{                    

protected:
  int read(const char* codice);

public:
  TObject* dup() const { return new TIVA(codice()); }

public:       
  const TString& codice() const { return get("CODTAB");}  
  const TString& descrizione() const { return get("S0"); } 
  const real aliquota() const { return get_real("R0"); } 
  const TString& tipo() const { return get("S1"); } 

  TIVA(const char* codice = NULL);
  TIVA(const TRectype& rec);
  virtual ~TIVA() {}
};

class TExpr_documento : public TExpression
{     
  TDocumento * _doc; 
  TRiga_documento * _row;
  
protected:                       
  virtual void evaluate_user_func(int index, int nparms, TEval_stack & stack, TTypeexp type) const;
  virtual int parse_user_func(const char * name, int nparms) const;

public:            
  // @cmember Duplica l'espressione
  virtual TObject* dup() const;
  // @cmember Assegna il documento corrente
  void set_doc(TDocumento * doc) { _doc = doc; }
  // @cmember Assegna il documento corrente
  void set_row(TRiga_documento * row) { _row = row; }
  // @cmember Costruttore (assegna l'estressione e il suo tipo)
  TExpr_documento(const char* expression, TTypeexp type = _numexpr,
                  TDocumento * doc = NULL, TRiga_documento * row = NULL);
  // @cmember Costruttore (assegna il tipo dell'istruzione) 
  TExpr_documento(TTypeexp type = _numexpr,
                  TDocumento * doc = NULL, TRiga_documento * row = NULL)
                 : TExpression(type), _doc(doc), _row(row) {}
  // @cmember Costruttore di copia
  TExpr_documento(const TExpr_documento & expr)
                 : TExpression(expr), _doc(expr._doc), _row(expr._row) {}
  // @cmember Distruttore
  virtual ~TExpr_documento() {}
};

enum TTipo_formula { _documento, _riga };

class TFormula_documento : public TRectype
{               
  TString16       _tab;
  TExpr_documento * _expr;
  
protected:
  int read(const char* codice);

public:
  TObject* dup() const { return new TFormula_documento(_tab == "%FRD" ? _documento : _riga, codice()); }

public:       
  const TString& codice() const { return get("CODTAB");}  
  const TString& name() const { return codice();}
  TExpr_documento * expr() const { return _expr ? new TExpr_documento(*_expr) : NULL;}
  
  const TString& descrizione() const { return get("S0"); } 
  const TString& expr_string() const { return get("S1"); }
  TTypeexp expr_type() const { return get_bool("B0") ? _numexpr : _strexpr;}
  
  TFormula_documento(TTipo_formula tipo = _documento, const char* codice = NULL);
  TFormula_documento(const TRectype& rec);
  virtual ~TFormula_documento();
};


class TTipo_documento : public TRectype
{              

  static TAssoc_array _formule_documento;
  TToken_string _formule;
  TString16 _imponibile;
  TString16 _imposta;
  TString16 _totale;
  TString16 _basesconto; 
  TString16 _spese;
        
protected:     
  void read_formule();
  int read(const char* tipodoc);

public:
  TObject* dup() const { return new TTipo_documento(codice()); }

public:       
  const TString& profile_name() const { return get("S4");}
  const TString& mask_name() const { return get("S4");}
  const TString& causale() const { return get("S6"); } 
  const TString& codice() const { return get("CODTAB");}  
  
  const TString& descrizione() const { return get("S0"); } 
  const TString& riferimento() const { return get("S1"); }
  const TString& imponibile() const { return _imponibile;}  
  const TString& imposta() const { return _imposta;}  
  const TString& totale_doc() const { return _totale;}  
  const TString& totale_netto() const { return TString("_") << _totale;}  
  const TString& basesconto() const { return _basesconto;}  
  const TString& spese() const { return _spese;}  

  TFormula_documento * first_formula() { return succ_formula(TRUE); }
  TFormula_documento * succ_formula(bool restart = FALSE);

  const int ncopie() const { return get_int("I0"); }
  
  TTipo_documento(const char* tipodoc = NULL);
  TTipo_documento(const TRectype& rec);
  virtual ~TTipo_documento();
};

class TTipo_riga_documento : public TRectype
{                    

  static TAssoc_array _formule_riga;
  TToken_string _formule;
  TString16 _name;
  TString16 _imponibile;
  TString16 _imposta;
  TVariable_mask * _mask;

protected:
  void read_formule();
  int read(const char* tiporig);

public:
  TObject* dup() const { return new TTipo_riga_documento(codice()); }

public:       
  const TString& profile_name() const { return _name;}
  const TString& mask_name() const { return profile_name();}
  const TString& codice() const { return get("CODTAB");}  
  bool mask_loaded() const { return _mask != NULL; }
  TVariable_mask * mask(); 
    
  const TString& descrizione() const { return get("S0"); } 
  char tipo() const { return get_char("S7"); }
  const TString& imponibile() const { return _imponibile;}  
  const TString& imposta() const { return _imposta;}  

  TFormula_documento * first_formula() { return succ_formula(TRUE); }
  TFormula_documento * succ_formula(bool restart = FALSE);
  
  TTipo_riga_documento(const char* tiporig = NULL);
  TTipo_riga_documento(const TRectype& rec);
  virtual ~TTipo_riga_documento();
};


class TRiga_documento : public TAuto_variable_rectype
{
  TDocumento * _doc; 
  static TAssoc_array _tipi;
  static TAssoc_array _spese;
  static TAssoc_array _ive;


protected:  
   // @cmember Setta il contenuto del campo <p fieldname> (non tipizzata)
  virtual void put_str(const char* fieldname, const char* val);

protected:
  TObject* dup() const { return new TRiga_documento(*this); }
  
public:  
  void dirty_fields(bool   dirty_document = TRUE); 
  bool doc_dependent() const;
  int numero() const { return get_int("NRIGA");}
  void set_numero(int numero) { put("NRIGA", numero);}
  // @cmember Assegna il documento corrente
  void set_doc(TDocumento * doc) { _doc = doc; }
  const TDocumento & doc() const { CHECK(_doc, "Documento nullo"); return *_doc;}

  virtual void zero(const char * fieldname);
  virtual void zero(char c = '\0');
  // row -> sheet
  void autoload( TSheet_field& f);
  // sheet -> row
  void autosave( TSheet_field& f);

  const TTipo_riga_documento & tipo() const;
  const TSpesa_prest & spesa() const;
  static const TIVA & iva(const char * codice);
  const TIVA & iva() const {const TString16 cod(get("CODIVA")); return iva(cod);}
  
  void set_tipo(const char * tipo) { put("TIPORIGA", tipo);}
  bool sola_descrizione() const;
  void forza_sola_descrizione();
  
  virtual TRectype & operator =(const TRectype & r); 
  virtual TRectype & operator =(const char * r); 
  
  bool raggruppabile(const TRiga_documento& r, TToken_string& campi) const;
  TRiga_documento& operator +=(const TRiga_documento& r);
  
  void reset_fields(TAuto_variable_rectype & rec) { rec.remove_field(); }
  void set_fields(TAuto_variable_rectype & rec);

  real prezzo(bool scontato, bool lordo, int ndec) const ;
  real importo(bool scontato, bool lordo, int ndec, bool iva_calc = FALSE) const ;   
  real iva(int ndec) const {return ::iva(imponibile(), iva(), ndec);}
  real imponibile() const; 
  real imposta() const;
  
  TRiga_documento(TDocumento* doc, const char * tipo = NULL);
  TRiga_documento(const TRiga_documento& rec, TDocumento* doc,
                  const char * tipo = NULL);
  virtual ~TRiga_documento() {}
};

class TDocumento : public TAuto_variable_rectype
{ 
  static TAssoc_array _tipi;

  TRecord_array _rows;  // Array di TRectype per le righe documenti di vendita.
  bool _nuovo;

  TRelation * _rel;
  TCond_vendita * _condv;
  TPagamento _pag;

protected:  
  TAuto_variable_rectype & row(int index) { return (TAuto_variable_rectype &) _rows.row(index, FALSE); } 
  long get_next_key(char provv, int anno, const char* codnum) const;
  virtual void put_str(const char* fieldname, const char* val);
  long renum(long numdoc = 0);

public:
  void dirty_fields();
  const TAuto_variable_rectype& head() const { return *this; } // Ritorna la testata del documento
  TAuto_variable_rectype& head() { return *this; } // Ritorna la testata del documento

  TCond_vendita & condv() const {CHECK(_condv, "Condizioni di vendita nulle"); return *_condv;}
  TRelation & get_relation() const {CHECK(_rel, "Relazione nulla"); return *_rel;}
  void set_condv(TCond_vendita * condv) { _condv = condv; }
  void set_relation(TRelation * rel) { _rel = rel; }
    
  virtual TRectype & operator =(const TRectype & r); 
  virtual TRectype & operator =(const char * r); 
  virtual void zero(const char * fieldname);
  virtual void zero(char c = '\0');
    
  int rows() const { return _rows.rows(); } 
  const TRiga_documento& operator[](int index) const { return (const TRiga_documento&)_rows.row(index); }
  TRiga_documento& operator[](int index) { return (TRiga_documento&)_rows.row(index, FALSE); } 
  
  TRiga_documento& insert_row(int row, const char *tipo = NULL);
  TRiga_documento& new_row(const char *tipo = NULL);
  bool destroy_row(int n, bool pack = FALSE) { return _rows.destroy_row(n, pack); }
  void destroy_rows() { _rows.destroy_rows(); }
  
  int read(char provv, int anno, const char* codnum, long numdoc);
  int read(const TRectype& rec);
  
  int write(bool re = FALSE) const;
  int rewrite() const { return write(TRUE); }
  int remove() const;

  char tipo_numerazione() const { return get_char("PROVV"); }
  int anno() const { return get_int("ANNO"); }
  const TString& numerazione() { return get("CODNUM"); }
  long numero() const { return get_long("NDOC"); }
  TDate data() const { return get_date("DATADOC"); }
  const bool in_valuta() const;
  const TString& valuta() const { return get("CODVAL"); }
  const real cambio() { return get_real("CAMBIO"); }
  const TTipo_documento& tipo() const;                  
  void set_tipo(const char * tipo) { head().put("TIPODOC", tipo);}
  
  char stato() const { return get_char("STATO"); }
  void stato(char s) { put("STATO", s); }
  
  bool raggruppabile() const { return get_bool("RAGGR"); }
  bool raggruppabile(const TDocumento& doc, TToken_string& campi) const;

  static void set_key(TRectype& rec, char provv, int anno, const char* codnum, long numdoc);
  static void copy_data(TRectype& dst, const TRectype& src);
  
  void reset_fields(TAuto_variable_rectype & rec) { rec.remove_field(); }
  void set_fields(TAuto_variable_rectype & rec);


  real spese_incasso(real & imp, int ndec, bool netto = FALSE) const ;
  real bolli(real & imp, int ndec, bool netto = FALSE) const ;
  real descrizione() const;
  real riferimento() const;
  real imponibile() const;
  real imposta() const;
  real totale_doc() const;
  real totale_netto() const;
  real basesconto() const;
  real spese() const;
  
  TPagamento & pagamento();

  TDocumento ();
  TDocumento(char provv, int anno, const char* codnum, long numdoc, 
             TCond_vendita * condv = NULL, TRelation * rel = NULL);
  TDocumento(const TRectype& doc, TCond_vendita * condv = NULL,
             TRelation * rel = NULL);
  virtual ~TDocumento() { }
};

class TDocumento_mask : public TVariable_mask
{
  int _progs_page;  // pagina in cui cominciano i progressivi
  int _last_prog;   // numero dell'ultimo progressivo
  TDocumento * _doc; // documento                    

protected:
  virtual void next_page(int p);

public:
  virtual bool on_key(KEY key);
  TDocumento & doc() const {CHECK(_doc, "Documento nullo"); return *_doc;}
  TCond_vendita & condv() const {CHECK(_doc, "Documento nullo"); return _doc->condv();}
  TRelation & get_relation() const {CHECK(_doc, "Documento nullo"); return _doc->get_relation();}
  
  TDocumento_mask(const char* name, TDocumento * _doc, int num = 0, int max = MAX_PAGES);
  virtual ~TDocumento_mask() { }
};

class TLista_documenti : public TObject
{ 
  TArray _documenti;
  
protected:  
  const TDocumento& doc(int n) const { return (const TDocumento&)_documenti[n]; }
  TDate num2date(char provv, int anno, const char* codnum, long num) const;

public:
  int read(char provv, char tipo, long clifo, int anno, 
           TToken_string& tipidoc, TToken_string& statidoc,
           const TDate& dd, const TDate& ad, 
           const char* codnum = "", long dn = 0L, long an = 0L);
  int write(bool re = FALSE) const;
  int rewrite() const { return write(TRUE); }
  
  int add(TDocumento* doc) { return _documenti.add(doc); }
  
  const TDocumento& operator[] (int n) const { return doc(n); }
  TDocumento& operator[] (int n) { return (TDocumento&)_documenti[n]; }
  int items() const { return _documenti.items(); }

  TLista_documenti() { }
  virtual ~TLista_documenti() { }
};

class TLista_clifo : public TObject
{                
  class TClifo : public TObject
  { 
    long _codice;
    long _agente;
    long _zona;
    
  protected:
    void zero() { _codice = _agente = _zona = 0L; }  
    void init(const TRectype& rec, const TRectype& ven); 
    bool read(char tipo, long cod);
  
  public: // TObject
    virtual bool ok() const { return _codice > 0; }
  
  public:     
    long codice() const { return _codice; }
    long agente() const { return _agente; }
    long zona() const { return _zona; }
  
    TClifo() { zero(); }
    TClifo(char tipo, long cod) { read(tipo, cod); }
    TClifo(const TRectype& rec, const TRectype& ven) { init(rec, ven); }
    TClifo(const TRectype& rec);
    virtual ~TClifo() { }
  };

  TArray _clifo;
  
protected:  
  virtual char tipo() const pure;
  const TClifo& clifo(int n) const { return (TClifo&)_clifo[n]; }
  
  static int sort_by_code(const TObject** o1, const TObject** o2);
  static int sort_by_agent(const TObject** o1, const TObject** o2);
  static int sort_by_zone(const TObject** o1, const TObject** o2);

public:
  int ordina_per_codice();
  int ordina_per_agente();
  int ordina_per_zona();
  
  int leggi(long dc, long ac, long da = 0, long aa = 0, long dz = 0, long az = 0);
  
  long operator[] (int n) const { return clifo(n).codice(); }
  int items() const { return _clifo.items(); }
  int find(long cod) const;
  int add(long cod);

  TLista_clifo() { }
  virtual ~TLista_clifo() { }
};

class TLista_clienti : public TLista_clifo
{
protected:
  virtual char tipo() const { return 'C'; }
};

class TLista_fornitori : public TLista_clifo
{
protected:
  virtual char tipo() const { return 'F'; }
};

class TElaborazione : public TRectype
{ 
protected:
  int read(const char* cod);

public:
  const TString& codice() const { return get("CODTAB"); }

  bool doc_uguale(int u) const  { return get("S1")[u] == 'X'; }
  bool riga_uguale(int u) const { return get("S1")[40+u] == 'X'; }
  bool raggruppa_righe() const      { return get_bool("B0"); }
  bool gestione_riferimenti() const { return get_bool("B1"); }
  bool riferimenti_in_testa() const { return get_bool("B2"); }
  bool ignora_descrizioni() const   { return get_bool("B3"); }
  
  char tipo_numerazione() const { return get_bool("B4") ? 'P' : 'D'; }
  const TString& codice_numerazione_finale() const { return get("S6"); }
  
  virtual bool elabora(TLista_documenti& doc_in, TLista_documenti& doc_out,
                       const TDate& data_elab) pure;

  TElaborazione(const char* cod);
  TElaborazione(const TRectype& rec) : TRectype(rec) { }
  virtual ~TElaborazione() { }
};

class TFatturazione_bolle : public TElaborazione
{ 
  TToken_string _cod_desc;

public:
  virtual bool raggruppa(TDocumento& din, TDocumento& dout);
  virtual bool elabora(TLista_documenti& doc_in, TLista_documenti& doc_out,
                       const TDate& data_elab);
                       
  void tipi_validi(TToken_string& tipi) const;                     
  void stati_validi(TToken_string& stati) const;                     
  
  TFatturazione_bolle(const char* cod);
  virtual ~TFatturazione_bolle() { }
};

#endif
