// 770200 - collegamento 1a nota (versamento) - ritenuta percipienti

#include <applicat.h>
#include <mailbox.h>
#include <relation.h>    

#include "77lib.h"
#include "770200a.h"

class TVersa_rit : public TSkeleton_application
{ 
  private:
    TLink770      _coll;
    real            _versato;
    TRelation*      _rel;
    TCursor*        _cur;
    TCursor_sheet*  _sheet_perc;
    TArray_sheet*   _schede;
    TArray_sheet*   _pagam;
    TMask*          _msk;    
    TLocalisamfile* _perc, *_scperc, *_rpag, *_rver;
    TAssoc_array    _apags;
// Estremi versamento
    TString16     _data, _serie, _numero;
    char      _luogo, _tipo;

    void add(const long codditta, char tipoa, const long codanagr, 
             const int nprog, TToken_string& rriga);
    TToken_string* find(const long codditta, char tipoa, const long codanagr, 
                        const int nprog);
    void remove(const long codditta, char tipoa, const long codanagr, 
                const int nprog);
    static void work_tipoluogo(TMask_field& f);
    static bool luo_hndl (TMask_field& f, KEY k);      
    static bool tipo_hndl(TMask_field& f, KEY k);      
    static bool abicab_hndl(TMask_field& f, KEY k);
    
  protected:    
    virtual bool create();
    virtual bool destroy();
    virtual void main_loop();

    int         split_rpag(const long codditta,char tipoa,
                            const long codanagr, const int nprog, 
                            const int nriga, real& versato, real& da_versare,
                            const long newvers);
    int         attach_rpag(const long codditta, char tipoa,
                             const long codanagr, const int nprog,
                             const int nriga, const long new_vers);    
    long        add_new_vers(const long codditta, char tipoa,
                             const long codanagr, const int nprog,
                             const real& versato);
                              
    void        attach_pag_vers(TToken_string& r, real& versato, bool last);
    
    real        ritenuta_versata(const long codditta,const long numvers);
    bool        ha_pagamenti_non_versati(const long codditta,
                             char tipoa,const long codanagr,const int nprog);    
    TCursor*    meik_curs(TRelation* rel);
    bool        do_all();
    void    build_schede_sheet(const long codditta);
    void    build_pagam_sheet(const long codditta);
          
  public:
    TVersa_rit() {}
    ~TVersa_rit() {}
};

TCursor* TVersa_rit::meik_curs(TRelation* rel)
{
  TString16 filt;               
  TCursor*  cur;
  const long codditta = get_firm();
  filt.format("CODDITTA=%ld", codditta);   
  cur = new TCursor(rel, filt);
  return cur;  
}

bool TVersa_rit::create()
{ 
//  simulo una chiamata da contabilit�
//  (lo lascio per eventuali prove)
//  real totdocla = ZERO;
//  real spesela = ZERO;
//  real compensola = ZERO;
//  real impostela = ZERO;
//  real ritenutela = 70000;
//  TToken_string s(80);
//  s.add(4);
//  s.add("M");
//  s.add("F");
//  s.add(30010);
//  s.add(1);
//  s.add("2");
//  s.add("02-09-1997");
//  s.add(totdocla.string());
//  s.add(spesela.string());    
//  s.add(compensola.string());    
//  s.add(impostela.string());    
//  s.add(ritenutela.string());   
//  const char* name = "770 -1";
//  TMessage mla(name, "LINK770", s);                                                      
//  mla.send();     

// Collegamento da contabilita'
  TMailbox m;
  TMessage* msg = m.next_s("LINK770");

// Questo programma si richiama solo dalla contabilita'
  if (!msg) 
    return FALSE;
  if ( !_coll.read(msg->body()) )
    return warning_box("Errore nei parametri passati");
  _perc     = new TLocalisamfile(LF_PERC);
  _scperc   = new TLocalisamfile(LF_SCPERC);
  _rpag     = new TLocalisamfile(LF_RPAG);
  _rver     = new TLocalisamfile(LF_RVER);
  _rel      = new TRelation(LF_PERC);        
  _rel->add(LF_ANAG, "TIPOA=TIPOA|CODANAGR=CODANAGR");
  _cur      = meik_curs(_rel); 
  
  _msk      = new TMask("770200a");
  _msk->set_handler(F_LUOGO,  luo_hndl);
  _msk->set_handler(F_TIPO,   tipo_hndl);
  _msk->set_handler(F_SERIE,  abicab_hndl);
  _msk->set_handler(F_NUMERO, abicab_hndl);
      
  _msk->set(F_DATA,    _coll._datadoc);
  _msk->set(F_VERSATO, _coll._ritenute.string());


  _sheet_perc = new TCursor_sheet(_cur,
                                  " |TIPOA|CODANAGR|6->RAGSOC",
                                  "Selezione percipienti",              
                                  "@1|Tipo|Codice|Ragione sociale@50",
                                  8, 3);
  _schede = new TArray_sheet(3, 3, -3, -3,
                             "Selezione schede",
                             "@1|T|Codice|Scheda n.|Data@10|Ragione@50");  
  _pagam  = new TArray_sheet(3, 3, -3, -3,
                             "Selezione ritenute da versare",
                             "@1|T|Codice|Scheda n.|Riga n.|Ritenuta@18V");
  
  _apags.destroy();
  
  return TSkeleton_application::create();
}

bool TVersa_rit::destroy()
{
  delete _rel;
  delete _cur;
  delete _msk;
  delete _schede;
  delete _pagam;
  delete _sheet_perc;                     
  delete _rver; delete _rpag; delete _scperc; delete _perc;
  return TApplication::destroy();
}

void TVersa_rit::work_tipoluogo(TMask_field& f)
{
    TMask&  m       = f.mask();
    char    tipo    = m.get(F_TIPO)[0];
    char    luogo   = m.get(F_LUOGO)[0];
                        
    m.hide (-2);            // nasconde tutto                    

    if (tipo == 'D')
    {
      if (luogo == 'T') 
        m.show(-3);     // SOLO quietanza
      else 
        m.show(-4);     // serie e numero
    }
    else
      if (tipo == 'B')
        m.show (-6);    // ABI e CAB
      else
        if (tipo == 'C')
          m.show (-5);  // SOLO numero versamento
}

bool TVersa_rit::luo_hndl(TMask_field& f, KEY k)
{
  if (k == K_TAB)
    work_tipoluogo(f);
    
  if (k == K_ENTER)
  {
    TMask&  m       = f.mask();
    char    tipo    = m.get(F_TIPO)[0];
    char    luogo   = m.get(F_LUOGO)[0];

// Se ho indicato il luogo => devo indicare anche il TIPO
    if (isalpha(luogo)) 
      return tipo == ' ' || tipo == '\0' ? 
        f.warning_box("Indicare il tipo del versamento") : TRUE;
  }
  return TRUE;
                               
}

bool TVersa_rit::tipo_hndl(TMask_field& f, KEY k)
{
  if (k == K_TAB)
    work_tipoluogo(f);

  if (k == K_ENTER || k == K_TAB)
  {
    TMask&  m       = f.mask();
    char    tipo    = m.get(F_TIPO)[0];
    char    luogo   = m.get(F_LUOGO)[0];
    
// Se ho indicato il tipo => devo indicare anche il LUOGO
    if (isalpha(tipo)) 
      if (luogo == ' ' || luogo == '\0') 
        f.warning_box("Indicare il luogo del versamento");
  }
  return TRUE;
}
                             
bool TVersa_rit::abicab_hndl(TMask_field& f, KEY k)
{
  if (f.to_check(k))
  {
    TMask& m  = f.mask();
    const char tipo = m.get(F_TIPO)[0];

// ABI/CAB solo se tipo e' B
    if (tipo != 'B')
      return TRUE;
          
    const TString& park = f.get();
    for (int i=0; park[i]; i++)
      if (!isdigit(park[i]))
        return f.warning_box("Il codice ABI/CAB deve essere numerico");
  }
  return TRUE;
}

real TVersa_rit::ritenuta_versata(const long codditta,const long numvers)
{
  TLocalisamfile rver(LF_RVER);
  real  versato = ZERO;

  rver.zero();
  rver.setkey(2);
  rver.put("CODDITTA", (long)codditta);
//  rver.put("NUMVERS",  (long)numvers);
  if (rver.read() == NOERR)
    versato = rver.get_real("RITENUTA");

  return versato;
}

void TVersa_rit::add(const long codditta, char tipoa, const long codanagr, 
                     const int nprog, TToken_string& rrr)
{
  TString80 key;
  key.format("%5ld%c%5ld%d", codditta,tipoa,codanagr,nprog);
  _apags.add(key, rrr);
}

TToken_string* TVersa_rit::find(const long codditta, char tipoa, 
                                const long codanagr, const int nprog)
{
  TString80 key;
  key.format("%5ld%c%5ld%d", codditta,tipoa,codanagr,nprog);
  TToken_string* rr = (TToken_string*)_apags.objptr(key);
  return rr;
}

void TVersa_rit::remove(const long codditta, char tipoa, const long codanagr, 
                        const int nprog)
{
  TString80 key;
  key.format("%5ld%c%5ld%d", codditta,tipoa,codanagr,nprog);
  _apags.remove(key);
}


bool TVersa_rit::ha_pagamenti_non_versati(const long codditta,char tipoa,const long codanagr,const int nprog)
{
  TLocalisamfile rpag(LF_RPAG);
  bool found = FALSE;
  long numvers = 0L; 
  real ritvers = ZERO, ritpag = ZERO;
  int  nriga = 0;
  
  rpag.zero();
  rpag.put("CODDITTA", (long)codditta);
  rpag.put("TIPOA",    tipoa);
  rpag.put("CODANAGR", (long)codanagr);
  rpag.put("NPROG",    nprog);

  TRectype dep(rpag.curr());
  TToken_string rriga(80);
                            
  rpag.read(_isgteq);
                            
  while (rpag.curr() == dep && !rpag.eof())
  { 
    numvers = rpag.get_long("NUMVERS"); 
    nriga   = rpag.get_int("NRIGA");
    ritpag  = rpag.get_real("RITENUTA");
    if (numvers <= 0)
    {
      found = TRUE; 
      rriga.add(nriga);
      rriga.add(ritpag.string());
    }
/* 4.7.95 Considero solo i pagamenti NON collegati
    else 
    {
      ritvers = ritenuta_versata(codditta,numvers);     
      real resto = ritpag - ritvers;
      if (resto > ZERO)
      {
        found = TRUE;
        rriga.add(nriga);        
        rriga.add(resto.string());        
      }
    }
***************************************************************/    
    rpag.next();
  }

  if (found) 
    add(codditta,tipoa,codanagr,nprog,rriga);
  
  return found;
}

void TVersa_rit::build_pagam_sheet(const long codditta)
{                           
  _pagam->destroy();
// build array_sheet dei pagamenti non collegati o non del tutto versati
    int nriga = 0;
    
    for (int i=0; i < _schede->items(); i++)
      if (_schede->checked(i))
      {
        TToken_string& r = _schede->row(i);
        char tipoa       = r.get_char(1);
        long codanagr    = r.get_long(2);
        const int nprog  = r.get_int(3);

        TToken_string* tpag = find(codditta,tipoa,codanagr,nprog);
      
        if (!tpag) continue;
      
        while ( (nriga = tpag->get_int()) != 0 )  // le righe partono da 1
        {
          const real da_versare = tpag->get();  // leggo importo rimasto da versare
                    
          TToken_string rr(100);
          rr.add(" ");     // Spazio per selezionare
          rr.add(tipoa);
          rr.add(codanagr);
          rr.add(nprog);
          rr.add(nriga);
          rr.add(da_versare.string()); // Importo senza punti e virgole !!!!!!!!!!!!!
          _pagam->add(rr);        
        } 
      }
}           

void TVersa_rit::build_schede_sheet(const long codditta)
{
// Costruisco lista schede    
  _schede->destroy();

  TLocalisamfile sch(LF_SCPERC);
  sch.setkey(1);
  
  for (int i=0; i<_sheet_perc->items(); i++)
    if (_sheet_perc->checked(i))
    {
// Leggo tipo e codice del percipiente selezionato
      TToken_string& r = _sheet_perc->row(i);
      char tipoa    = r.get_char(1);
      long codanagr = r.get_long(2);
      TString80 ragsoc(r.get(3));
      
      sch.zero();
      sch.put("CODDITTA", (long)codditta);
      sch.put("TIPOA",     tipoa);
      sch.put("CODANAGR",  codanagr);
      TRectype dep(sch.curr());

// Leggo tutte le schede del percipiente scelto      
      sch.read(_isgteq);
      while (sch.curr() == dep && !sch.eof())
      {
        const int nprog = sch.get_int("NPROG");
        TString16 datadoc(sch.get("DATADOC"));
        
// Considero solo le schede con pagamenti non ancora versati        
        if (ha_pagamenti_non_versati(codditta,tipoa,codanagr,nprog))
        {
          TToken_string rr(100);
          rr.add(" ");     // Spazio per selezionare
          rr.add(tipoa);
          rr.add(codanagr);
          rr.add(nprog);
          rr.add(datadoc); 
          rr.add(ragsoc);
          _schede->add(rr);        
        }
        sch.next();
      } 
    }  // if checked
}    

bool TVersa_rit::do_all()
{

  long  items = 0L;                                       
  
  // esecuzione prima maschera: richiesta estremi versamento  
  KEY kp = _msk->run();                   
  
  if (kp != K_ENTER) 
    return FALSE;
  else
  {
    _data    = _msk->get(F_DATA);
    _luogo   = _msk->get(F_LUOGO)[0];
    _tipo    = _msk->get(F_TIPO)[0]; 
    _versato = real(_msk->get(F_VERSATO));
    if (_tipo == DELEGA_BANCARIA)
    {
      _serie = _msk->get(F_ABI);
      _numero = _msk->get(F_CAB);
    }
    else
    {
      _serie   = _msk->get(F_SERIE);
      _numero  = _msk->get(F_NUMERO);
    }
  }
  
  // loop di gestione sheet di scelta percipiente,
  // scelta scheda e scelta pagamento da versare 
  while (TRUE)
  {                 
    items = _sheet_perc->items();
    
    if (items == 0L)
    {
      warning_box("Nessun percipiente registrato");
      return FALSE;
    }              
    
    kp = _sheet_perc->run();
    if (kp == K_ENTER)
    {
      if (!_sheet_perc->one_checked())    
      {
        warning_box("Nessun percipiente selezionato");
        continue;
      }          
    }
    else
      return FALSE;

    const long selected = _sheet_perc->selected();
    const long codditta = get_firm();  

    build_schede_sheet(codditta);
    
    items = _schede->items();
    
    if (items == 0L)
    {
      warning_box("Non esistono pagamenti non versati per i percipienti selezionati");
      continue;
    }              
    
    kp = _schede->run();
    if (kp == K_ENTER)
    {
      if (!_schede->one_checked())    
      {
        warning_box("Nessuna scheda selezionata");
        continue;
      }          
    }
    else
      continue;

    build_pagam_sheet(codditta);
          
    kp = _pagam->run();

    if (kp == K_ENTER)                             
      if (!_pagam->one_checked())    
      {
        warning_box("Nessun pagamento selezionato");
        continue;
      }          
    
    if (kp == K_ESC) 
      continue;
                   
// Leggo importo arrivato dalla contabilita'
//    _versato = _coll._totdoc;
    
// Distribuisce _versato sui pagamenti selezionati creando la riga di 
// versamento se occorre             
    const long totchk = _pagam->checked();
    if (totchk > 0)
    {
      long chk = 0L;
      for (int i=0; i < _pagam->items() && _versato > ZERO; i++) if (_pagam->checked(i))
      {
        chk++;
        const bool is_last_checked = (chk == totchk);
        TToken_string& r = _pagam->row(i);    
        attach_pag_vers(r, _versato, is_last_checked);
      }
      return warning_box("Creazione di %d versamenti terminata", chk);
    }
  } // while (TRUE)
      
  return TRUE;
}

void TVersa_rit::attach_pag_vers(TToken_string& r, real& disponibile, bool is_last)
{
  const long codditta = get_firm();
  const char tipoa    = r.get_char(1);
  const long codanagr = r.get_long(2);
  const int  nprog    = r.get_int(3);
  const int  nriga    = r.get_int(4);
  const real da_versare = r.get(5);  // importo ancora "scoperto" in questo pag.
    
  real vers_corr;
  // Se e' l'ultimo pagamento metto tutto l'importo del versamento  
  if (is_last)
    vers_corr = disponibile;
  else
    vers_corr = (disponibile > da_versare) ? da_versare : disponibile;
  disponibile -= vers_corr;  // Scalo versamento corrente

  const long new_vers = add_new_vers(codditta,tipoa,codanagr,nprog,vers_corr);
  attach_rpag(codditta,tipoa,codanagr,nprog,nriga,new_vers);
}

int TVersa_rit::split_rpag(const long codditta,char tipoa,
                            const long codanagr, const int nprog, 
                            const int nriga, real& versato, real& da_versare,
                            const long new_vers)
{
  TLocalisamfile rpag(LF_RPAG);
  long numpag = 0L;   
  long pag    = 0L;           
  int  new_riga = 0;
  int  ret_code = 0;
  
// Determino un nuovo NUMPAG  
  for (rpag.first(); !rpag.eof(); rpag.next())
  {
    pag  = rpag.get_long("NUMPAG");  
    if (pag > numpag) numpag = pag;
  }                         
  numpag++;

// Determino un nuovo NRIGA
  rpag.zero();
  rpag.put("CODDITTA", (long)codditta);
  rpag.put("TIPOA",    tipoa);
  rpag.put("CODANAGR", (long)codanagr);
  rpag.put("NPROG",    nprog);
  
  TRectype dep(rpag.curr());
  rpag.read(_isgteq);
  while (rpag.curr() == dep && !rpag.eof())
  {
    new_riga = rpag.get_int("NRIGA");
    rpag.next();
  }
  new_riga++;
  
// Sposto il pagamento preesistente
  rpag.zero();
  rpag.put("CODDITTA", (long)codditta);
  rpag.put("TIPOA",    tipoa);
  rpag.put("CODANAGR", (long)codanagr);
  rpag.put("NPROG",    nprog);
  rpag.put("NRIGA",    nriga);  
  
  if ((ret_code = rpag.read()) != NOERR)
    return ret_code;

// Leggo ritenuta del vecchio record e il suo versamento eventuale  
// La parte che era stata gia' versata resta con il vecchio record
// La parte versata ora viene registrata nel nuovo.
  real ritenuta      = rpag.get("RITENUTA");
  real parte_versata = ritenuta - da_versare;  
  
  TRectype salvo(rpag.curr());

  rpag.zero();              
  rpag.curr() = salvo;

// Cambio solo: NUMPAG, NRIGA, e aggiorno la ritenuta. NUMVERS resta uguale
  rpag.put("NUMPAG",   (long)numpag);
  rpag.put("NRIGA",    new_riga);  
  rpag.put("RITENUTA", parte_versata);

// Registro il vecchio pagamento nella nuova posizione
  if ((ret_code = rpag.write()) != NOERR) 
    warning_box("Pagamento non inserito. Codice %d", ret_code);

// Modifico l'originale per puntare al nuovo versamento
  rpag.zero();              
  rpag.curr() = salvo;
  rpag.put("NUMVERS", (long) new_vers);
  rpag.put("RITENUTA", da_versare);

// Salvo le modifiche al vecchio pagamento
  if ((ret_code = rpag.rewrite()) != NOERR) 
    warning_box("Pagamento non modificato. Codice %d", ret_code);
    
  return ret_code;  
}
                            
int TVersa_rit::attach_rpag(const long codditta, char tipoa,
                            const long codanagr, const int nprog,
                            const int nriga, const long new_vers)
{
  TLocalisamfile rpag(LF_RPAG);
  rpag.zero();
  rpag.put("CODDITTA", codditta);
  rpag.put("TIPOA",    tipoa);
  rpag.put("CODANAGR", codanagr);
  rpag.put("NPROG",    nprog);
  rpag.put("NRIGA",    nriga);         
  
  int ret_code = rpag.read();
  if (ret_code != NOERR) 
  {
    error_box("Pagamento non trovato. Errore %d", ret_code);
    return ret_code;
  }
  rpag.put("NUMVERS", new_vers);  
  
  ret_code = rpag.rewrite();
  if (ret_code != NOERR) 
  {
    error_box("Pagamento non modificato. Errore %d", ret_code);
    return ret_code;
  }                          

  return ret_code;
}
                             
long TVersa_rit::add_new_vers(const long codditta, char tipoa,
                              const long codanagr, const int nprog,
                              const real& versata)
{
  TLocalisamfile rver(LF_RVER);
  int nriga = 0;

// Determino un numero di riga nuovo
  rver.zero();
  rver.put("CODDITTA", (long)codditta);
  rver.put("TIPOA",    tipoa);
  rver.put("CODANAGR", (long)codanagr);
  rver.put("NPROG",    nprog);
  
  TRectype dep(rver.curr());
  rver.read(_isgteq);
  while (rver.curr() == dep && !rver.eof())
  {
    nriga = rver.get_int("NRIGA");
    rver.next();
  }
  nriga++;
  
// Scrivo il nuovo record
  rver.zero();
  rver.put("CODDITTA", codditta);
  rver.put("TIPOA",    tipoa);
  rver.put("CODANAGR", codanagr);
  rver.put("NPROG",    nprog);
  rver.put("NRIGA",    nriga);   
//  rver.put("NUMVERS", (long)nriga);
  rver.put("DATAVERS", _data);
  rver.put("LUOVERS",  _luogo);
  rver.put("TIPOVERS", _tipo);
  rver.put("SERIE",    _serie);
  rver.put("NUMERO",   _numero);
  rver.put("RITENUTA", versata);

  if (rver.write() != NOERR)
  {
    error_box("Fallita creazione versamento. Errore %d", rver.status());
    return 0L;
  }
  
  TLocalisamfile scperc(LF_SCPERC);
  scperc.zero();
  scperc.put("CODDITTA", codditta);
  scperc.put("TIPOA",    tipoa);
  scperc.put("CODANAGR", codanagr);
  scperc.put("NPROG",    nprog);
  if (scperc.read() == NOERR)
  {
    real ritver = scperc.get("RITVER");
    ritver += versata;                 
    scperc.put("RITVER", ritver);
    scperc.rewrite();
  }
  
  return nriga;
}

void TVersa_rit::main_loop()
{
  do_all(); 
}

int collega_vers_rit(int argc, char* argv[])
{   
  TVersa_rit a;
  a.run(argc, argv, "Versamento ritenute");
  return 0;
}