#include <dongle.h>
#include <modaut.h>

#include "velib04.h"

///////////////////////////////////////////////////////////
// TFatturazione bolle
///////////////////////////////////////////////////////////

TFatturazione_bolle::TFatturazione_bolle(const char* cod) 
                   : TElaborazione(cod)
{
}

void TFatturazione_bolle::tipi_validi(TToken_string& tipi) const
{
  tipi.cut(0);
  TString4 t;
  for (int i = 0; i < TElaborazione::_max_tipi_doc_elab; i++)
  {
    t = tipo_iniziale(i);
    if (t.not_empty())
      tipi.add(t);
  }
  CHECK(!tipi.empty_items(), "Nessun tipo documento valido");
}

void TFatturazione_bolle::stati_validi(TToken_string& stati) const
{
  stati.cut(0);
  for (int i = 0; i < TElaborazione::_max_tipi_doc_elab; i++)
  {
    const char s = stato_iniziale(i);
    if (s != '\0')
      stati.add(s);
  }
  CHECK(!stati.empty_items(), "Nessuno stato documento valido");
}


bool TFatturazione_bolle::raggruppa(TDocumento& doc_in, TDocumento& doc_out)
{                 
#ifdef DBG
  const TString4 tipodoc = doc_in.tipo().codice();
  int i;
                   
  for (i = 0; i < TElaborazione::_max_tipi_doc_elab; i++)
  {                  
    if (tipodoc == tipo_iniziale(i))
      break;
  } 
  if (i >= TElaborazione::_max_tipi_doc_elab)
  {
    NFCHECK("Tipo documento non valido: '%s'", (const char*)tipodoc);
    return true;
  }  
#endif

  const TDate dcons = doc_in.get_date(DOC_DATACONS);
	const TDate ddoc = doc_out.get_date(DOC_DATADOC);
	const usa_dcons = usa_data_consegna();

	if (usa_dcons)
	{
		bool da_elaborare = FALSE;
	  for (int r = 1; !da_elaborare && r <= doc_in.physical_rows(); r++)            
		{
	    const TRiga_documento* rin = &doc_in[r];
			TDate data_cons = rin->get_date(RDOC_DATACONS);

			if (!data_cons.ok())
				data_cons = dcons;
			da_elaborare = ddoc >= data_cons && !rin->get_bool(RDOC_RIGAEVASA);
		}
		if (!da_elaborare)
			return FALSE;
	}

//***vecchio posto cambio stato           

  if (gestione_riferimenti())
  { 
    // Determina ed eventualmente crea la riga di riferimento
    const int riga_rif = riferimenti_in_testa() ? 1 : doc_out.physical_rows()+1;
    if (riga_rif > doc_out.physical_rows())
    {
      TRiga_documento& rout = doc_out.new_row();
      rout.forza_sola_descrizione();
    }  
    
    TRiga_documento& rout = doc_out[riga_rif];     
    
    // Costruisce la stringa di riferimento
    TString riferimento;
    doc_in.riferimento(riferimento);
    if (riferimento.empty()) 
      riferimento = doc_in.tipo().descrizione();
    if (usa_doc_rif() && doc_in.get(DOC_NUMDOCRIF).not_empty())
    {
      riferimento << " n. " << doc_in.get(DOC_NUMDOCRIF);
      riferimento << " del " << doc_in.get(DOC_DATADOCRIF);
    }
    else
    {
      riferimento << " n. " << doc_in.numero();
      riferimento << " del " << doc_in.data().string();
    }
    
    // Setta la descrizione se vuota
    if (rout.get(RDOC_DESCR).empty()) 
      rout.put(RDOC_DESCR, riferimento);
    else
    {                               
      // Altrimenti aggiungi il riferimento al memo
      TString memo(1024);
      memo = rout.get(RDOC_DESCEST);
      if (memo.empty())
      { 
        TString80 rif(rout.get(RDOC_DESCR)); 
        rif << '\n';
        rout.put(RDOC_DESCR, rif);
        rout.put(RDOC_DESCLUNGA, "X");
      }
      else
        memo << '\n';
      memo << riferimento;
      rout.put(RDOC_DESCEST, memo);
    }  
  }

  const bool ignora_desc = ignora_descrizioni();

  TToken_string campi_riga(80);   
  const bool ragg_rig = raggruppa_righe();
  if (ragg_rig)
  {
    // Uguali sempre
    campi_riga.add(RDOC_CODART);
    campi_riga.add(RDOC_UMQTA);  
    // Uguali se commesse attive
    if (dongle().active(CAAUT) || dongle().active(CMAUT))
    {
      campi_riga.add(RDOC_CODCMS);
      campi_riga.add(RDOC_FASCMS);
      campi_riga.add(RDOC_CODCOSTO);
    }
    // Uguali opzionalmente
    if (riga_uguale(0)) 
      campi_riga.add(RDOC_CODMAG);
    if (riga_uguale(1)) 
      campi_riga.add(RDOC_CODIVA);
    if (riga_uguale(2)) 
    {
      campi_riga.add(RDOC_PREZZO);
      campi_riga.add(RDOC_SCONTO);
    }
  }
        
  for (int r = 1; r <= doc_in.physical_rows(); r++)            
  {                
    TRiga_documento & rin = doc_in[r];
    const bool rindesc = rin.sola_descrizione(); // La riga di input e' descrittiva
    if (ignora_desc && rindesc)
      continue;
      
		if (usa_dcons)
		{
			TDate data_cons = rin.get(RDOC_DATACONS);
			if (!data_cons.ok())
				data_cons = dcons;
			if (ddoc < data_cons || rin.get_bool(RDOC_RIGAEVASA))
				continue;
			
			const real q = rin.get(RDOC_QTA);

			rin.put(RDOC_QTAEVASA, q);
			rin.put(RDOC_RIGAEVASA, true);
		}

    bool elaborata = false;
    
    // Raggruppo le righe se e' settato il flag di raggruppamento e 
    // se la riga non contiene solo una descrizione
    if (ragg_rig && !rindesc)                    // Se devo raggruppare le righe ...
    {              
      const int last = doc_out.physical_rows();
      for (int o = 1; o <= last; o++)            // ... cerca una riga compatibile
      {
        TRiga_documento& rout = doc_out[o];  
        if (rout.sola_descrizione())             // Ignora le righe descrittive
          continue;
        
        if (rin.raggruppabile(rout, campi_riga)) // Se esiste una riga compatibile ...
        {
          rout += rin;                           // ... sommaci la quantita' ecc.
          elaborata = true;                      // Ricorda di averla gia' elaborata
          break;
        }  
      }  
    }
    if (!elaborata)                              // Se la riga non e' stata gia' sommata ...
    {
      TRiga_documento& rout = doc_out.new_row(); // ... crea una riga nuova e 

      doc_out.copy_data(rout, rin);             // copiaci tutti i campi della riga sorgente.
      rout.set_original_rdoc_key(rin);          // memorizza il codice della riga originale
			
      if (usa_dcons)
			{
				rout.zero(RDOC_QTAEVASA);
				rout.zero(RDOC_RIGAEVASA);
			}
      
      if (kill_descrizione_estesa())             // Cancello eventualmente la descrizione estesa
      {
        rout.zero(RDOC_DESCLUNGA);
        rout.zero(RDOC_DESCEST);
      }

			if (prezzo_da_ordine())  // Se devo copiare il prezzo originale all'ordine
			{
				const TRectype* row_ord = rin.find_original_rdoc();
				if (row_ord != NULL)
				{                       
					const real ord_price = row_ord->get_real(RDOC_PREZZO);
					const TString& ord_scont = row_ord->get(RDOC_SCONTO);
					rout.put(RDOC_PREZZO, ord_price);
					rout.put(RDOC_SCONTO, ord_scont);
				}
			}
    }  
  }

  //cambio stato documento
  bool cambia_stato_doc_in = true;
  if (usa_dcons)
  {
    for (int r = 1; r <= doc_in.physical_rows(); r++)            
    {                
      const TRiga_documento & rin = doc_in[r];
      if (!rin.sola_descrizione() && !rin.get_bool(RDOC_RIGAEVASA)) 
		  {
        cambia_stato_doc_in = false;
        break;
		  }
    }
  }

  if (cambia_stato_doc_in)
  {
    const char stato_finale_in = get_char("S4");
	  doc_in.stato(stato_finale_in);
  }  
	const char stato_finale_out = get_char("S9");
	doc_out.stato(stato_finale_out);


	if (usa_dcons)
		doc_in.rewrite();
  
  return TRUE;
}

bool TFatturazione_bolle::elabora(TLista_documenti& doc_in, TLista_documenti& doc_out,
                                  const TDate& data_elab, bool interattivo)
{       
  TWait_cursor hourglass;  

  TToken_string campi_doc(128);             // Lista di campi che devono essere uguali
  campi_doc = "TIPOCF|CODCF|CODVAL|CODLIN"; // Uguali sempre
      
  // Uguali opzionalmente
  const char* cond[] = { "CAMBIO", "SCONTO", "TIPODOC", "CODNUM",
                         "CODPAG", "CODABIA|CODCABA", "CODLIST", "CODAG",
                         "CODSPMEZZO", "CODPORTO", "CAUSTRASP", "CODVETT1|CODVETT2|CODVETT3", 
                         "CODINDSP",
                         NULL };
  
  for (int u = 0; cond[u]; u++)  
    if (doc_uguale(u)) campi_doc.add(cond[u]);

  for (int id = 0; id < doc_in.items(); id++)
  {                                             
    TDocumento& campione = doc_in[id];
    const int tot = doc_out.items();
    int od = tot;                    
    
    if (interattivo)
    {                                              
      od = 0;  
      const char tipo = campione.get_char(DOC_TIPOCF);
      const long codice = campione.get_long(DOC_CODCF);  
      
      if (tipo != doc_out[od].get_char(DOC_TIPOCF) ||
          codice != doc_out[od].get_long(DOC_CODCF))
        return error_box("Documenti incompatibili : cliente/fornitore diverso");
    }
    else
    {
      if (campione.raggruppabile())    // Se il documento ha il flag di raggruppabilita' ...
      {
        for (od = 0; od < tot; od++)   // ... cerca un documento compatibile.
        {
          if (campione.raggruppabile(doc_out[od], campi_doc))
            break;
        }
      }
    }  

    if (od >= tot)   // Se non ho trovato un documento compatibile ...
    {                // ... creane uno nuovo (certamente compatibile)                  
      const int anno   = data_elab.year();
      const TString4 codnum(campione.get("CODNUM"));
      const TString4 tipo_out(get("S8"));  // Tipo del documento di output
      TDocumento* new_doc  = new TDocumento('D', anno, codnum, -1);

      // Attenzione! Il cambio del tipo documento provocherebbe il reset delle variabili
      // Per cui lo scrivo temporaneamente nel tipo del documento d'ingresso
//      TRecfield td(campione, DOC_TIPODOC);                      // Uso il TRecfield per scavalcare tutti gli automatismi
//      const TString16 old_tipo_in = td;                         // Salvo il vecchio tipo
//      td = tipo_out;                                            // Setto il nuovo
      TDocumento::copy_data(new_doc->head(), campione.head());  // Copio la testata 
//      td = old_tipo_in;                                         // Ripristino il vecchio
      
      new_doc->put("DATADOC", data_elab);
 
      // Aggiungilo alla lista dei documenti in uscita
      od = doc_out.add(new_doc);               
    }                      

		if (!raggruppa(campione, doc_out[od]) && doc_out[od].physical_rows() == 0)
      doc_out.destroy(od);

    campione.flush_rows();
  }

  const int tot = doc_out.items();
  const TString4 codnum(codice_numerazione_finale());
  const TString4 tipo_out(get("S8"));  // Tipo del documento di output
  
  for (int i = 0; i < tot; i++)   // Forza tipo e numerazione documento.
  {
    TDocumento & d = doc_out[i];
    d.put("CODNUM", codnum);
    
    TToken_string key; key.add(d.get(DOC_TIPOCF)); key.add(d.get(DOC_CODCF));          
    const TRectype & cfven = cache().get(LF_CFVEN, key);              
    const TString4 tipo_cli(cfven.get(CFV_TIPODOCFAT)); 

    TRecfield td(d, DOC_TIPODOC); // Uso il TRecfield per scavalcare tutti gli automatismi
    td = tipo_cli.empty() ? tipo_out : tipo_cli;
    const TString& sconto = d.get(DOC_SCONTOPERC);
    d.put(DOC_SCONTOPERC, sconto);
  }
  return tot > 0;
}