// Esercizi contabili e registri IVA

#include <mask.h>
#include <prefix.h>
#include <tabutil.h>
#include <utility.h>

#include "cglib01.h"

///////////////////////////////////////////////////////////
// Gestione Tabella esercizi
///////////////////////////////////////////////////////////

TArray TEsercizi_contabili::_esercizi;
long TEsercizi_contabili::_firm = 0;

TEsercizio::TEsercizio(const TRectype& rec)
{                   
  _codice   = rec.get_int("CODTAB");
  _inizio   = rec.get("D0");
  _fine     = rec.get("D1");
  _scarico  = rec.get("D2");
  _chiusura = rec.get("D3");
  _chiusura_mag = rec.get("D4");
}

int TEsercizio::compare(const TSortable& s) const
{
  const TEsercizio& e = (const TEsercizio&)s;
  int c = 0;
  if (_inizio != e._inizio) 
    c = _inizio > e._inizio ? +1 : -1;
  return c;  
}

TEsercizi_contabili::TEsercizi_contabili()
{ 
}

void TEsercizi_contabili::update()
{       
  _firm = prefix().get_codditta();
  
  _esercizi.destroy();
  TTable tab_esc("ESC");
  for (int err = tab_esc.first(); err == NOERR; err = tab_esc.next())
  {
    TEsercizio* e = new TEsercizio(tab_esc.curr());
    _esercizi.add(e);
  }              
  _esercizi.sort();
} 

void TEsercizi_contabili::check()
{
  if (_firm != prefix().get_codditta())
  {
#ifdef DBG   
    if (_firm != 0)
      error_box("Questo programma carinissimo usa gli esercizi,\n"
                "purtroppo non tiene conto del cambio ditta!");
#endif  
    update();
  }
}

int TEsercizi_contabili::date2index(const TDate& d) const
{
  check();    
  for (int i = items()-1; i >= 0; i--)
  {     
    const TEsercizio& e = esc(i);
    if (d >= e.inizio() && d <= e.fine())
      break;
  }
  return i;
}

int TEsercizi_contabili::esc2index(int codice) const
{
  check();    
  for (int i = items()-1; i >= 0; i--)
  {     
    const TEsercizio& e = esc(i);
    if (codice == e.codice())
      break;
  }
  return i;
}

int TEsercizi_contabili::date2esc(const TDate& d) const
{
  const int i = date2index(d);
  return i >= 0 ? esc(i).codice() : 0;
}

int TEsercizi_contabili::first() const
{
  check();
  return items() ? esc(0).codice() : 0;
}

int TEsercizi_contabili::last() const
{
  check();
  return items() ? esc(items()-1).codice() : 0;
}

int TEsercizi_contabili::pred(int codice) const
{
  const int i = esc2index(codice);
  return i > 0 ? esc(i-1).codice() : 0;
}

int TEsercizi_contabili::next(int anno) const
{
  const int i = esc2index(anno);
  return i < items()-1 ? esc(i+1).codice() : 0;
}

bool TEsercizi_contabili::exist(int codice) const
{
  const int i = esc2index(codice);
  return i >= 0;
}

const TEsercizio& TEsercizi_contabili::esercizio(int codice) const
{
  const int i = esc2index(codice);
  return esc(i);
}

const char* iva2name(TipoIVA iva)
{
  const char* i;
  switch(iva)
  {
  case nessuna_iva: 
    i = "Nessuna IVA"; break;
  case iva_acquisti: 
    i = "IVA Acquisti"; break;
  case iva_vendite: 
    i = "IVA Vendite"; break;
  case iva_generica: 
    i = "IVA Generica"; break;
  default: 
    i = "IVA ERRATA!"; break;
  }
  return i;
}

///////////////////////////////////////////////////////////
// Registro
///////////////////////////////////////////////////////////

TRegistro::TRegistro(const char* cod, int year) : _rec(LF_TAB), _att(LF_ATTIV)
{
  read(cod, year);
}


bool TRegistro::read(const char* cod, int year)
{               
  if (year <= 0) 
  {
    const TDate oggi(TODAY);
    year = oggi.year();
  }

  int err = ~NOERR;
  
  TTable reg("REG");
  reg.setkey(1);
  if (cod && *cod > ' ')
  {
    TString16 chiave; chiave.format("%04d%s", year, cod);
    reg.put("CODTAB", chiave);
    err = reg.read(); 
  }
  _rec = reg.curr();
  
  if (err != NOERR)
    _rec.zero();
  read_att();  

  return err == NOERR;
}           

bool TRegistro::reread()
{
  if (ok()) 
  {
    const TString16 n(name());
    const int y = year();
    return read(n, y);
  }  
  return FALSE;
}              

int TRegistro::year() const 
{                
  TString16 anno(_rec.get("CODTAB")); 
  anno.cut(4);
  return atoi(anno);
}   


const TString& TRegistro::name() const 
{ 
  return _rec.get("CODTAB").mid(4);
}


TRegistro& TRegistro::operator =(const TRegistro& r)
{ 
  _rec = r._rec; 
  _att = r._att;
  _prorata = r._prorata;
  return *this;
}


int TRegistro::tipo() const
{
  const int t = _rec.get_int("I0");
  return t;
}


bool TRegistro::corrispettivi() const
{
  const bool c = _rec.get_bool("B0");
  return c;
}


TipoIVA TRegistro::iva() const
{ 
  TipoIVA i = (TipoIVA)tipo();
  switch (i)
  {
  case nessuna_iva:
  case iva_vendite:
  case iva_acquisti:
    break;
  case libro_giornale:
    i = nessuna_iva;
    break;
  default:  
    error_box("Il registro '%s' non e' un registro IVA o contabile: tipo %d", 
              (const char*)name(), i);
    i = nessuna_iva;
    break;
  }
  return i;     
} 

bool TRegistro::read_att()
{ 
  if (!_att.empty()) 
    return TRUE;

  TLocalisamfile attiv(LF_ATTIV);
  attiv.setkey(1);
  attiv.put("CODDITTA", prefix().get_codditta());
  attiv.put("CODATT", attivita());
  const int err = attiv.read();
  _att = attiv.curr();
  if (err != NOERR)
    _att.zero();
    
  TString16 chiave;             // Ditta - Anno - Attivita' - Tipo Attivita' (fissata a 1)
  chiave.format("%05ld", prefix().get_codditta());
  chiave << year();             // non fare << year() << attivita()
  chiave << attivita() << "1";
    
  TTable pla("%PLA");
  attiv.setkey(1);
  pla.put("CODTAB", chiave);
  if (pla.read() == NOERR)
  {
    _prorata = pla.get_real("R8");
    _att.put("TIPOATT", pla.get("S7")); // Aggiorna tipo attivita'
  }  
  else  
    _prorata = 0.0;  
    
  return err == NOERR;
}

bool TRegistro::agenzia_viaggi()
{                             
  bool av = FALSE;
  if (iva() == iva_vendite) 
    av = _att.get_bool("REG74TER");
  return av;
}

const TString& TRegistro::tipo_attivita()
{
  return _att.get("TIPOATT");
}

const real& TRegistro::prorata()
{ 
  return _prorata;
}

void TRegistro::set_prorata(const real& pro)
{
  _prorata = pro;
}

// Certified 99%                       
bool TRegistro::update(long protiva, const TDate& datareg)
{ 
  bool updated = TRUE;
  
  if (protiva > _rec.get_long("I5"))
  {
    _rec.put("I5", protiva);
    updated = FALSE;
  }  
  if (datareg > _rec.get_date("D2"))
  {
    _rec.put("D2", datareg);
    updated = FALSE;
  }  
  if (!updated)
  { 
    TTable reg("REG");
    updated = reg.rewrite(_rec) == NOERR;
  }  
  
  return updated;
}

///////////////////////////////////////////////////////////
// Libro giornale
///////////////////////////////////////////////////////////

// Legge il libro giornale dell'anno specificato
bool TLibro_giornale::read(int y)
{
  bool found = FALSE;                 

  if (y <= 0) 
  {
    const TDate oggi(TODAY);
    y = oggi.year();
  }
  
  TString16 anno; anno.format("%04d", y);
  TTable reg("REG");
  reg.setkey(1);                                  
  reg.put("CODTAB", anno);                    // Cerca il primo registro dell'anno
  
  for (int err = reg.read(_isgteq); err == NOERR; err = reg.next())
  {
    if (reg.get("CODTAB").compare(anno, 4) != 0) 
      break;                                  // Sono arrivato all'anno dopo
    
    if (reg.get_int("I0") == libro_giornale)
    {       
      found = TRUE;
      break;
    }  
  }
  
  if (!found) reg.zero();                     // Memorizza record (anche vuoto)
  _rec = reg.curr();
  
  return found;
}

TLibro_giornale::TLibro_giornale(int y)
{
  read(y);
}

///////////////////////////////////////////////////////////
// Codice IVA
///////////////////////////////////////////////////////////

TCodiceIVA::TCodiceIVA(const char* cod) : TRectype(LF_TABCOM)
{
  read(cod);
}                 

bool TCodiceIVA::read(const char* cod)
{   
  int err = ~NOERR;
  if (cod && *cod)
  {
    TTable iva("%IVA");
    iva.put("CODTAB", cod);
    err = iva.read();
    TRectype::operator=(iva.curr());
  }   
  if (err != NOERR) 
    zero();
  return err == NOERR;
}               

real TCodiceIVA::scorpora(real& imponibile) const
{
  const real percent = percentuale();
  real imposta = abs(imponibile) * percent / (percent + 100.0); imposta.ceil();
  if (imponibile.sign() < 0) imposta = -imposta; 
  imponibile -= imposta;
  return imposta;
}  

///////////////////////////////////////////////////////////
// TBill
///////////////////////////////////////////////////////////

TBill::~TBill() 
{
  if (_descrizione)
    delete _descrizione;
}

void TBill::set_description(const char* d)
{
  if (_descrizione || (d && *d))
  {
    if (_descrizione)
      *_descrizione = d;
    else
      _descrizione = new TString(d);
  }
}

// Certified 90%
const TBill& TBill::get(TToken_string& s, int from, int mode)
{
  const char* first = s.get(from);
  if (mode & 0x1)
  {
    _tipo = first ? toupper(*first) : ' ';
    first = s.get();
  } else _tipo = ' ';

#ifdef DBG
  if (strchr(" CF", _tipo) == NULL)
  {
    error_box("Tipo conto errato: '%c'", _tipo);
    _tipo = ' ';
  }
#endif

  _gruppo = first ? atoi(first) : 0;
  _conto = s.get_int();
  _sottoconto = s.get_long();
  if (mode & 0x2)
    set_description(s.get());
    
  _tipo_cr = -1;
  _sezione = ' ';
  
  return *this;
}

const TBill& TBill::copy(const TBill& bill)
{
  _tipo = bill._tipo;
  _gruppo = bill._gruppo;
  _conto = bill._conto;
  _sottoconto = bill._sottoconto;
  set_description(bill.descrizione());
  _tipo_cr = bill._tipo_cr;
  _sospeso = bill._sospeso;
  _sezione = bill._sezione;
  return *this;
}


// Certified 100%
const TBill& TBill::set(int g, int c, long s, char t, const char* d, int r)
{
  _tipo = (t > ' ') ? toupper(t) : ' ';
  _gruppo = g;
  _conto = c;
  _sottoconto = s;
  set_description(d);
  _tipo_cr = r;
  return *this;
}

const TBill& TBill::add_to(TToken_string& ts, int from, int mode)
{
  if (mode & 0x4) 
  {
    const int cr = tipo_cr();
    if (cr > 0) ts.add(cr, from++); else ts.add(" ", from++);
  }  
  
  if (mode & 0x1) 
    ts.add(_tipo, from++);
  
  if (_gruppo > 0) ts.add(_gruppo, from++); else ts.add(" ", from++);
  if (_conto > 0) ts.add(_conto, from++); else ts.add(" ", from++);
  if (_sottoconto > 0L) ts.add(_sottoconto, from++); else ts.add(" ", from++);
  
  if (mode & 0x2) 
    ts.add(descrizione(), from++);
  
  return *this;  
}


const char* TBill::field_name(int n, bool contro) const
{             
  CHECKD(n >= 0 && n <= 3, "Invalid bill field", n);        

  const char* f;
  if (contro)
  {
    switch(n)
    {
    case 0: f = "GRUPPOC"; break;
    case 1: f = "CONTOC"; break;
    case 2: f = "SOTTOCONTC"; break;
    default:f = "TIPOCC"; break;
    }
  }  
  else
  {
    switch(n)
    {
    case 0: f = "GRUPPO"; break;
    case 1: f = "CONTO"; break;
    case 2: f = "SOTTOCONTO"; break;
    default:f = "TIPOC"; break;
    }
  }  
  return f;
}

void TBill::put(TRectype& r, bool c) const
{  
  r.put(field_name(0, c), gruppo());
  r.put(field_name(1, c), conto());
  r.put(field_name(2, c), sottoconto());
  r.put(field_name(3, c), tipo());
}

bool TBill::get(const TRectype& r, bool c)
{ 
  set(r.get_int(field_name(0, c)), 
      r.get_int(field_name(1, c)),
      r.get_long(field_name(2, c)), 
      r.get_char(field_name(3, c)));
  
  set_description(NULL);
  _tipo_cr = -1;
  _sezione = ' ';
  
  if (r.num() == LF_RMOVIVA)      
    tipo_cr(r.get_int("TIPOCR"));
  
  return ok();
}

void TBill::set(TMask& m, short g, short c, short s, short t, short d) const
{
  m.set(g, gruppo());
  m.set(c, conto());
  m.set(s, sottoconto());
  if (t) 
  {
    char typ[2] = { tipo(), '\0' };
    m.set(t, typ);
  }  
  if (d)
    m.set(d, descrizione());
}

void TBill::get(const TMask& m, short g, short c, short s, short t, short d)
{
  const int gr = m.get_int(g);
  const int co = m.get_int(c);
  const long so = m.get_long(s);
  char ti = ' ';
  if (t) 
    ti = m.get(t)[0];
  TString80 de;
  if (d)  
    de = m.get(d);
  set(gr, co, so, ti, de);
}


// Certified 100%
bool TBill::ok() const
{
  return _gruppo != 0 && _conto != 0 && _sottoconto != 0L;
}

// Certified 99%
int TBill::compare(const TSortable& s) const
{
  CHECK(class_name()==s.class_name(), "Can't compare TBill with TObject");
  const TBill& c = (const TBill&)s;

  int res = _gruppo - c._gruppo;
  if (res) return res;

  res = _conto - c._conto;
  if (res) return res;

  const long lres = _sottoconto - c._sottoconto;
  if (lres < 0L) res = -1; else
    if (lres > 0L) res = +1;

  return res;
}


// Certified 95%
bool TBill::find()
{   
  bool ok = FALSE;

  if ((_tipo != 'C' && _tipo != 'F') || _sottoconto == 0L)
  {                 
    TRectype pcon(LF_PCON);
    ok = read(pcon);
  } 
  else
    if ((_tipo == 'C' || _tipo == 'F') && _sottoconto != 0L)
    {
      TLocalisamfile clifo(LF_CLIFO);
      clifo.setkey(1);
      clifo.put("TIPOCF", _tipo);
      clifo.put("CODCF", _sottoconto);
      ok = clifo.read() == NOERR;
      if (ok)
      {        
        set_description(clifo.get("RAGSOC"));
        if (_tipo_cr < 0) 
        {
          _tipo_cr = 0;
          _sezione = ' ';
        }  
        _sospeso = clifo.get_bool("SOSPESO"); 
        
        const char tipoa = clifo.get_char("TIPOAPER");
        if (tipoa == 'F')  // Se persona fisica allora aggiusta la ragione sociale
        {
          TString nome(descrizione().mid(30));
          if (nome.not_empty())
          {
            _descrizione->cut(30);
            _descrizione->trim(); nome.trim();
            *_descrizione << ' ' << nome;
          }  
        }
        if (_gruppo == 0 || _conto == 0)
        {
          _gruppo = clifo.get_int("GRUPPO");
          _conto = clifo.get_int("CONTO");
        }
      }  
    }
  
  return ok;
}


bool TBill::read(TRectype &r)
{
  TLocalisamfile pcon(LF_PCON);
  pcon.put("GRUPPO", _gruppo);
  pcon.put("CONTO", _conto);
  pcon.put("SOTTOCONTO", _sottoconto);

  const int err = pcon.read();
  if (err == NOERR)
  {
    r = pcon.curr();
    _tipo_cr = r.get_int("TIPOSPRIC");    
    _sezione = r.get_char("SEZSALDI");
    set_description(r.get("DESCR"));
    _sospeso     = r.get_bool("SOSPESO"); 
  }  
  else
    r.zero();  
  
  return err == NOERR;
}

int TBill::tipo_att()
{
  int tipo_att = 1;
  if (tipo() <= ' ' && ok())               
  { 
    TBill bill(gruppo(), conto());
    TRectype rec(LF_PCON); bill.read(rec);
    const TIndbil ib = (TIndbil)rec.get_int("INDBIL");
    if (ib == ib_passivita || ib == ib_ricavi)
    {
      read(rec);
      const int ricser = rec.get_int("RICSER");  // 0 = Altre attivita  1 = Servizi
      tipo_att = (ricser == 1) ? 1 : 2;
    }
  }                      
  return tipo_att;
}  

// Certified 99%
const TString& TBill::descrizione() const
{                    
  TBill& myself = (TBill&)*this;
  // Se il conto e valido (c'e' almeno il gruppo) cerca la sua descrizione su file
  if ((_descrizione == NULL || _descrizione->blank()) && gruppo() != 0)
  {
    if (!myself.find()) 
      myself.set_description("Sconosciuto");
  }
  if (_descrizione == NULL)
    myself._descrizione = new TString;
  
  return *_descrizione;
}

int TBill::tipo_cr() const
{
  if (_tipo_cr < 0)               
  {
    TBill& myself = (TBill&)*this;
    myself.find();
  }  
  return _tipo_cr;
}

// Certified 99% (uses __tmp_string)
const char* TBill::string(int mode) const
{
  TFixed_string s(&__tmp_string[256], 80);
  s.cut(0);
  
  if (mode & 0x4)
  {
    const int cr = tipo_cr();
    if (cr > 0) s << cr << '|';
    else s << " |";
  }  
  
  if (mode & 0x1)
    s << _tipo << '|';
  
  if (_gruppo > 0) s << _gruppo  << '|';
  else s << " |";
  
  if (_conto > 0) s << _conto  << '|';
  else s << " |";
  
  if (_sottoconto > 0L) s << _sottoconto;
  else s << ' ';
  
  if (mode & 0x2) 
    s << '|' << descrizione();

  return s;
}