campo-sirio/src/tf/tf0100.cpp

1425 lines
40 KiB
C++
Raw Normal View History

#include <applicat.h>
#include <automask.h>
#include <config.h>
#include <execp.h>
#include <golem.h>
#include <progind.h>
#include <reputils.h>
#include <tabutil.h>
#include <utility.h>
#include <agasys.h>
#include "../ve/velib05.h"
#include "../cg/cglib03.h"
#include "tf0.h"
#include "tf0100a.h"
#include "../fe/felib.h"
#include <mov.h>
#include <rmoviva.h>
#include <anagiu.h>
#include <comuni.h>
#include <cfven.h>
#include <nditte.h>
#include <unloc.h>
#include <causali.h>
/////////////////////////////////////////////////////////////////////////////////////
// Globals
/////////////////////////////////////////////////////////////////////////////////////
static XVT_SQLDB _db = NULL; // TFF sqlite db
enum return_code
{
found, // Trovato
movcustom, // Trovato in trasfatt
nextmov, // Trovato ma cambiato movimento
eof, // EOF rmoviva
eofm, // EOF mov
after // File mov su un movimento dopo rispetto a rmoviva
};
enum filter_fatt
{
toSend, // Da inviare
sent, // Inviate
all // Tutte
};
/////////////////////////////////////////////////////////////////////////////////////
// Utilities
/////////////////////////////////////////////////////////////////////////////////////
// Cerca una stringa all'interno di una SLIST (Potrebbe diventare una funzione di XVT.h)
static SLIST_ELT xvt_slist_find_str(SLIST list, const char* str)
{
SLIST_ELT e = NULL;
for (e = xvt_slist_get_first(list); e; e = xvt_slist_get_next(list, e))
{
const char* val = xvt_slist_get(list, e, NULL);
if (xvt_str_compare_ignoring_case(str, val) == 0)
break;
}
return e;
}
static bool haveRFSO(TString& codrfso)
{
codrfso = cache().get(LF_NDITTE, prefix().firm().codice(), "CODRFSO");
if(codrfso == "") return false;
return true;
}
static bool hasRFSO(TString tipocf, TString codcli, TString& codrfso)
{
codrfso = cache().get(LF_CLIFO, tipocf << "|" << codcli, "CODRFSO");
if(codrfso == "") return false;
return true;
}
// DA CONTROLLARE!!!!!
static const char* decodTipo(TDocumento& doc)
{
if(doc.is_fattura())
return "TD01";
if(doc.is_nota_credito())
return "TD04";
return "TD05";
}
/////////////////////////////////////////////////////////////////////////////////////
// TTrFa_record
/////////////////////////////////////////////////////////////////////////////////////
// Contenitore di campi di un record di database SQLite
class TTrFa_record : public TObject
{
TString8 _table;
TToken_string _key;
TAssoc_array _fields;
protected:
void copy(const TTrFa_record& rec) { _table = rec._table; _key = rec._key; _fields = rec._fields; }
const TString& var2str(const TString& fld, const TVariant& var) const;
public:
void reset() { _fields.destroy(); }
void set(const char* fld, const TVariant& var);
void set(const char* fld, long var);
void set(const char* fld, const char* var);
void set(const char* fld, const real& var);
void set(const char* fld, const TString& var);
void set(const char* fld, const TDate& var);
void set(const char* fld, bool var);
const TVariant& get(const char* fld) const;
bool insert();
bool remove();
bool search();
bool search(const char* k1, const char* k2, const char* k3 = NULL);
virtual TObject* dup() const { return new TTrFa_record(*this); }
virtual bool ok() const { return _table.not_empty(); }
TTrFa_record& operator=(const TTrFa_record& rec) { copy(rec); return *this; }
TTrFa_record(const TTrFa_record& rec) { copy(rec); }
TTrFa_record(const char* table);
};
// Imposta il valore di un campo variant
void TTrFa_record::set(const char* fld, const TVariant& var)
{
CHECK(fld && *fld, "Null field name");
if (var.is_null())
{
_fields.remove(fld);
}
else
{
TVariant* obj = (TVariant*)_fields.objptr(fld);
if (obj != NULL)
*obj = var;
else
_fields.add(fld, new TVariant(var));
}
}
// Imposta il valore di un campo intero
void TTrFa_record::set(const char* fld, long val)
{
const TVariant var(val);
set(fld, var);
}
// Imposta il valore di un campo stringa
void TTrFa_record::set(const char* fld, const char* val)
{
if (val == NULL)
set(fld, NULL_VARIANT);
else
{
const TVariant var(val);
set(fld, var);
}
}
// Imposta il valore di un campo stringa
void TTrFa_record::set(const char* fld, const TString& val)
{
const TVariant var(val);
set(fld, var);
}
// Imposta il valore di un campo numerico
void TTrFa_record::set(const char* fld, const real& val)
{
const TVariant var(val);
set(fld, var);
}
// Imposta il valore di un campo data in formato ISO
void TTrFa_record::set(const char* fld, const TDate& val)
{
if (val.ok())
{
const TVariant var(val);
set(fld, var);
}
else
set(fld, "");
}
// Imposta il valore di un campo booleano
void TTrFa_record::set(const char* fld, bool var)
{
set(fld, var ? "SI" : "NO");
}
// Legge il valore di un campo variant
const TVariant& TTrFa_record::get(const char* fld) const
{
const TVariant* var = (const TVariant*)_fields.objptr(fld);
return var ? *var : NULL_VARIANT;
}
// Converte un variant in una stringa valida per SQLite
const TString& TTrFa_record::var2str(const TString& fldname, const TVariant& var) const
{
const TFieldtypes vt = var.type();
if (vt == _realfld)
{
const TCurrency v(var.as_real(), "", ZERO, fldname.find("PRZ")>0 || fldname.find("PREZZO")>0);
TString& tmp = get_tmp_string();
tmp << '\'' << v.string() << '\''; tmp.replace(',','.');
return tmp;
}
if (vt == _datefld)
{
TString& tmp = get_tmp_string();
tmp << '\'' << var.as_date().string(full, '-', full, full, amg_date) << '\'';
return tmp;
}
const TString& str = var.as_string();
bool apici = vt == _alfafld;
if (apici && str[0] != '0' && real::is_natural(str))
apici = false;
if (!apici)
return str;
TString& tmp = get_tmp_string();
tmp = str;
for (int a = str.rfind('\''); a >= 0; a--)
{
if (tmp[a] == '\'')
tmp.insert("'", a);
}
tmp.insert("'", 0);
tmp << '\'';
return tmp;
}
// Elimina il record in base ai campi chiave
bool TTrFa_record::remove()
{
TString256 query;
query << "DELETE FROM " << _table << " WHERE ";
int nkf = 0;
FOR_EACH_TOKEN(_key, fld)
{
const TVariant& var = get(fld);
if (!var.is_null())
{
if (nkf++ > 0)
query << " AND ";
query << fld << '=' << var2str(fld, var) ;
}
}
CHECKS(nkf >= 2, "Can't remove partial key on table ", (const char*)_table);
query << ';';
return xvt_sql_execute(_db, query, NULL, 0L) > 0;
}
// Callback per la sottostante funzione search()
static int tff_search_record(void* jolly, int cols, char** values, char** names)
{
TTrFa_record& rec = *(TTrFa_record*)jolly;
for (int i = 0; i < cols; i++)
rec.set(names[i], values[i]);
return 0;
}
// Carica un record in base ai campi chiave
bool TTrFa_record::search()
{
CHECKS(_fields.items() >= _key.items(), "Can't search partial key on table ", _table);
TString256 query;
query << "SELECT * FROM " << _table << " WHERE ";
FOR_EACH_TOKEN(_key, fld)
{
const TVariant& var = get(fld);
if (!var.is_null())
query << fld << '=' << var2str(fld, var) << " AND ";
}
query.rtrim(5);
query << ';';
return xvt_sql_execute(_db, query, tff_search_record, this) == 1;
}
// Carica un record in base ad un massimo di 3 campi chiave
bool TTrFa_record::search(const char* k1, const char* k2, const char* k3)
{
_fields.destroy();
set(_key.get(0), k1);
set(_key.get(1), k2);
if (k3 && *k3)
set(_key.get(2), k3);
return search();
}
// Aggiunge un record al db
bool TTrFa_record::insert()
{
CHECKS(_fields.items() > _key.items(), "Can't insert empty record on table ", _table);
TString query, values;
query << "INSERT INTO " << _table << "\n(";
FOR_EACH_ASSOC_OBJECT(_fields, obj, fld, itm)
{
const TVariant& var = get(fld);
if (!var.is_null())
{
query << fld << ',';
values << var2str(fld, var) << ',';
}
}
query.rtrim(1); values.rtrim(1);
query << ")\nVALUES (" << values << ");";
return xvt_sql_execute(_db, query, NULL, 0L) == 1;
}
// Crea un record della tabella data ed imposta i nomi dei campi chiave
TTrFa_record::TTrFa_record(const char* table) : _table(table), _key(15, ',')
{
_key = ini_get_string("./tff.ini", table, "INDEX_1");
if (_key.empty())
{
// Cerco di costruire i nomi della chiave cercando la K, come in P1_KEYHEADERFATT
TConfig cfg("tff.ini", table);
TAssoc_array& fields = cfg.list_variables();
FOR_EACH_ASSOC_STRING(fields, obj, key, str)
{
if (key[3] == 'K')
_key.add(key);
}
}
CHECKS(!_key.empty_items(), "Invalid primary key for table ", table);
}
/////////////////////////////////////////////////////////////////////////////////////
// TTrFa_cursors
/////////////////////////////////////////////////////////////////////////////////////
/*
* Classe per la gestione dei cursori RMOVIVA e MOV
*/
class TTrFa_cursors : TObject
{
friend class TCursor;
TSorted_cursor* c_mov;
TCursor* c_moviva;
TCursor* c_trasfatt;
TRelation* r_mov;
TRelation* r_moviva;
TRelation* r_trasfatt;
filter_fatt filFat;
bool filOk(bool s) { return (filFat == all) || (filFat == sent && s) || (filFat == toSend && !s); }
int _next(bool init = false); // Si sposta avanti di un elemento
public:
TTrFa_cursors();
~TTrFa_cursors() { delete c_mov, c_moviva, r_mov, r_moviva, r_trasfatt, c_trasfatt; };
long int getMovItems() { return c_mov->items(); }
long int getIvaItems() { return c_moviva->items(); }
long int getMovPos() { return c_mov->pos(); }
long int getIvaPos() { return c_moviva->pos(); }
TRectype getMov() { return c_mov->curr(); }
TRectype getIva() { return c_moviva->curr(); }
TRectype getTrasfatt() { return c_trasfatt->curr(); }
int next(TAssoc_array& recimposte); // Legge tutto il prossimo movimento, in importi mette per ogni codiva la somma
int updateFilters(const char tipocf, const long codcf, TDate dal, TDate al, TAssoc_array& recimposte, int cod = toSend);
};
TTrFa_cursors::TTrFa_cursors() : filFat(all)
{
r_mov = new TRelation(LF_MOV);
r_moviva = new TRelation(LF_RMOVIVA);
r_trasfatt = new TRelation(LF_TRASFATT);
c_mov = new TSorted_cursor(r_mov, "NUMREG");
c_moviva = new TCursor(r_moviva);
c_trasfatt = new TCursor(r_trasfatt);
};
int TTrFa_cursors::next(TAssoc_array& recimposte)
{
int err = _next(true);
while(err < nextmov)
{
TRectype mov = getMov(), miva = getIva(), cust = getTrasfatt();
if(recimposte.is_key(miva.get("CODIVA")))
{
// Prelevo il record salvato
TRectype app = *(TRectype*)recimposte.objptr(miva.get("CODIVA"));
// Aggiorno i valori
app.put("IMPONIBILE", app.get_real("IMPONIBILE") + (err == movcustom ? cust.get_real("IMPONIBILE") : miva.get_real("IMPONIBILE")));
app.put("IMPOSTA", app.get_real("IMPOSTA") + (err == movcustom ? cust.get_real("IMPOSTA") : miva.get_real("IMPOSTA")));
// Lo reinserisco
recimposte.add(miva.get("CODIVA"), app, true);
}
else // Inserisco per la prima volta
{
// Creo un record di tipo tabella trasmissione fatture, tanto conterrebbe tutti i dati necessari
TRectype app(LF_TRASFATT);
// Inserisco i dati
if(err == movcustom)
{
app.put("NUMREG", cust.get("NUMREG"));
app.put("TIPO", cust.get("TIPO"));
app.put("COD", cust.get("CODCF"));
app.put("TIPODOC", cust.get("TIPODOC"));
app.put("NUMDOC", cust.get("NUMDOC"));
app.put("DATADOC", cust.get("DATADOC"));
app.put("IMPONIBILE", cust.get("IMPONIBILE"));
app.put("IMPOSTA", cust.get("IMPOSTA"));
app.put("CODIVA", cust.get("CODIVA"));
}
else
{
app.put("NUMREG", mov.get("NUMREG"));
app.put("TIPO", mov.get("TIPO"));
app.put("COD", mov.get("CODCF"));
app.put("TIPODOC", mov.get("TIPODOC"));
app.put("NUMDOC", mov.get("NUMDOC"));
app.put("DATADOC", mov.get("DATADOC"));
app.put("IMPONIBILE", miva.get("IMPONIBILE"));
app.put("IMPOSTA", miva.get("IMPOSTA"));
app.put("CODIVA", miva.get("CODIVA"));
}
// Salvo il record nell'array
recimposte.add(app.get("CODIVA"), app);
}
err = _next();
}
return err;
}
/*
* Questa funzione si sposta sui due cursori trovando la prossima riga, se le righe sono finite si posiziona sul prossimo movimento o ritorna EOF
*/
int TTrFa_cursors::_next(bool init)
{
bool isNextMov = false;
if(c_moviva->pos() == c_moviva->items())
return eof; // A noi interessa solo rmoviva
else
{
TRectype rigaMov = c_mov->curr(), rigaIva = c_moviva->curr(), rigaTF = c_trasfatt->curr();
bool first = true;
bool checkOk = false;
// Posso capitare movimenti aggiunti dopo che non rientrano nel periodo che ci interessa, applico un controllino
while(first || rigaMov.get_long("NUMREG") > rigaIva.get_long("NUMREG") || !filOk(checkOk))
{
first = false;
// Stratagemma terribile per evitare di saltare il primo record
if(!init)
{
if(!c_moviva->next_match(0))
return eof;
}
else init = false;
rigaMov = c_mov->curr();
rigaIva = c_moviva->curr();
rigaTF = c_trasfatt->curr();
while(rigaMov.get_long("NUMREG") < rigaIva.get_long("NUMREG"))
{
isNextMov = true; // Vale la pena fare un controllo aggiuntivo per questo? Il while verr<72> eseguito max 2 volte
if (!c_mov->next_match(0))
return eofm;
rigaMov = c_mov->curr();
}
// Mi muovo finch<63> le righe non sono le stesse
while(rigaTF.get_long("NUMREG") <= rigaIva.get_long("NUMREG") && rigaTF.get_long("NUMRIG") < rigaIva.get_long("NUMRIG") && c_trasfatt->pos() != c_trasfatt->items())
{
if(!c_mov->next_match(0)) // Non mi interessa se raggiungo la fine di questa tabella
break;
rigaTF = c_trasfatt->curr();
}
if(rigaTF.get_long("NUMREG") == rigaIva.get_long("NUMREG") && rigaTF.get_long("NUMRIG") == rigaIva.get_long("NUMRIG"))
checkOk = rigaTF.get_bool("TFINVIO");
else
checkOk = rigaIva.get_bool("TFINVIO");
}
if(isNextMov)
{
if(rigaTF.get_long("NUMREG") == rigaIva.get_long("NUMREG") && rigaTF.get_long("NUMRIG") == rigaIva.get_long("NUMRIG"))
return movcustom;
else
return nextmov;
}
else
return found;
}
}
int TTrFa_cursors::updateFilters(const char tipocf, const long codcf, TDate dal, TDate al, TAssoc_array& recimposte, int cod)
{
filFat = (filter_fatt)cod;
TRectype filMov(r_mov->curr());
int movKey = 1;
TString filter;
if(dal.empty()) dal = "20170101"; // Data in cui questo modulo <20> diventato valido
if(al.empty()) al = TODAY;
filter << "&&(BETWEEN(DATADOC," << dal.date2ansi() << ',' << al.date2ansi() << "))";
if(tipocf != 'T')
{
movKey = 3;
filMov.put("TIPO", tipocf);
// Se <20> selezionato un cliente specifico
if(codcf > 0)
filMov.put("CODCF", codcf);
}
else
filMov = 0;
// Risetto mov
delete c_mov;
c_mov = new TSorted_cursor(r_mov, "NUMREG", TString("REG!=\"\"")<<filter, movKey, &filMov, &filMov);
c_mov->items();
c_mov->first_item();
// Preparo c_moviva
TRectype filIva(r_moviva->curr());
filIva.put("NUMREG", TRectype(c_mov->curr()).get_long("NUMREG"));
// Resetto rmoviva
delete c_moviva;
c_moviva = new TCursor(r_moviva, "", 1, &filIva);
c_moviva->first_item();
// Preparo c_moviva
TRectype filTF(r_trasfatt->curr());
filTF.put("NUMREG", TRectype(c_mov->curr()).get_long("NUMREG"));
// Resetto trasfatt
delete c_trasfatt;
c_trasfatt = new TSorted_cursor(r_trasfatt, "NUMREG", "", 1, &filIva);
c_trasfatt->first_item();
// Mi posiziono sullo stesso Numero di registrazione
if(c_mov->items() > 0 && c_moviva->items() > 0)
{
return next(recimposte);
}
return eof;
}
/////////////////////////////////////////////////////////////////////////////////////
// TTrFa_mask
/////////////////////////////////////////////////////////////////////////////////////
class TTrFa_mask : public TAutomask
{
friend class TTrFa_cursors;
TMaskmode _mode;
bool _sheet_dirty;
bool _filter_changed;
protected:
virtual void next_page(int p);
const char * natura(const TString& codiva) const;
real get_IVA(const TString& codiva) const;
char revCharge(TString codcaus) const;
TString findDetraib(TString codiva) const;
virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);
//void fill();
//void init();
public:
void setFilterChanged() { _filter_changed = true; }
void load_sheet();
TTrFa_mask(TString msk) : TAutomask(msk), _filter_changed(true) {}
};
void TTrFa_mask::next_page(int p)
{
TAutomask::next_page(p);
if (_filter_changed)
{
TSheet_field & sf = sfield(F_RIGHE);
if (curr_win() == sf.parent())
{
load_sheet();
sf.force_update();
_filter_changed = false;
}
}
}
bool TTrFa_mask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
switch (o.dlg())
{
case F_DATAINI:
if (e == fe_init)
o.set(ini_get_string(CONFIG_DITTA, "tf", "LastSend", "01-01-2017"));
break;
case F_DATAFIN:
if (e == fe_init)
o.set(TDate(TODAY));
break;
case F_TIPOCF:
if(get(F_TIPOCF) != "T")
{
enable(F_CODCF);
enable(F_RAGSOC);
}
else
{
disable(F_CODCF);
disable(F_RAGSOC);
}
break;
case DLG_ALL:
if (e == fe_button)
{
TSheet_field& docs = sfield(F_RIGHE);
TString_array& sht = docs.rows_array();
const int items = sht.items();
if (items > 0)
{
const TString4 select = *(sht.row(0).get(0)) == 'X' ? "" : "X";
for (int i = 0; i < items; i++)
sht.row(i).add(select, 0);
docs.force_update();
}
}
break;
default:
break;
}
const short id = o.dlg();
if (e == fe_modify && jolly == 1)
{
if (id >= A_NUMERO && id < A_COFI && id != A_FORZATA)
{
o.mask().set(A_FORZATA, true);
}
}
if (e == fe_modify && jolly == 0)
{
if (id >= F_DATAINI && id <= F_FATTSEL)
{
setFilterChanged();
}
}
return true;
}
void TTrFa_mask::load_sheet()
{
const char tipocf = get(F_TIPOCF)[0];
const long codcf = get_long(F_CODCF);
TDate dal = get_date(F_DATAINI), al = get_date(F_DATAFIN);
//TString key; key << "TIPOA=" << prefix().firm().get("TIPOA")<< ",CODANAGR=" << prefix().firm().get("CODANAGR");
TString key; key << prefix().firm().get("TIPOA") << "|" << prefix().firm().get("CODANAGR");
const TString pivaDitta = cache().get(LF_ANAG, key, "PAIV");
const TString cofiDitta = cache().get(LF_ANAG, key, "COFI");
TTrFa_cursors c;
TSheet_field& sheet = sfield(F_RIGHE);
TString_array& strarr = sheet.rows_array();
sheet.hide(); // Nascondo lo sheet per guadagnare un 20% di velocit<69> di caricamento, le ottimizzazioni da PRO!
sheet.destroy();
TAssoc_array recimposte;
int next = c.updateFilters(tipocf, codcf, dal, al, recimposte, get_int(F_FATTSEL));
while(next < eof)
{
TRectype mov(c.getMov()), miva(c.getIva()), mcust(c.getTrasfatt());
TToken_string* row = new TToken_string;
// Carico i clienti
TRelation r_cli(LF_CLIFO);
TRectype filCli(r_cli.curr());
filCli.put("TIPOCF", mov.get("TIPO"));
filCli.put("CODCF", mov.get("CODCF"));
TCursor c_cli(&r_cli, "", 1, &filCli, &filCli); c_cli.items();
c_cli = 0;
TRectype cli(c_cli.curr());
FOR_EACH_ASSOC_OBJECT(recimposte, h, iva, rec)
{
TRectype movimento = *(TRectype*)rec;
row->add(next == movcustom ? mcust.get("TFINVIO") : miva.get("TFINVIO"), 0); //Forzo la posizione
row->add(row[0] == "X"? "" : "X"); // Se <20> gi<67> stata spedita la riga non viene preselezionata
row->add(next == movcustom ? "X" : "");
row->add(movimento.get_long("NUMREG"));
row->add(miva.get_long("NUMRIG"));
row->add(cli.get("TIPOCF"));
row->add(cli.get("CODCF"));
row->add(cli.get_bool("OCCAS") ? "X" : "");
row->add(cli.get("RAGSOC"));
row->add(cli.get("CODRFSO"));
row->add(movimento.get("TIPODOC")); // Tipo documento
row->add(movimento.get_long("NUMDOC")); // Numero documento
row->add(movimento.get_date("DATADOC")); // Data documento
row->add(natura(iva)); // NATURA!
row->add(get_IVA(iva)); // Aliquota!
row->add(findDetraib(iva)); // Detraibilit<69>
row->add(movimento.get_real("IMPONIBILE")); // Imponibile
row->add(movimento.get_real("IMPOSTA")); // Imposta
row->add(revCharge(mov.get("CODCAUS"))); // Rev.Charge
row->add(pivaDitta == cli.get("PAIV") ? 'X' : '\0'); // AutoFatt
if(strcmp(row->get(19), "X") == 0) // Se <20> un autofattura
{
row->add(pivaDitta);
row->add(cofiDitta);
}
else
{
row->add(cli.get("PAIV"));
row->add(cli.get("COFI"));
}
strarr.add(row);
}
recimposte.destroy();
next = c.next(recimposte);
}
sheet.force_update();
sheet.show();
}
TString TTrFa_mask::findDetraib(TString codiva) const
{
// Mi creo un cursore per i valori di indetraibilit<69>, cos<6F> facendo mi risparmio operazioni lente dopo
static TRelation r_indetr(LF_TABCOM);
static TRectype f_indetr(r_indetr.curr()); f_indetr.put("COD", "DET");
static TCursor c_indetr(&r_indetr, "", 1, &f_indetr, &f_indetr);
TString codtab = cache().get("%IVA", codiva, "S4");
if(codtab.full())
{
for(c_indetr = 0; c_indetr.pos() < c_indetr.items(); ++c_indetr)
{
TRectype row = c_indetr.curr();
if(codtab == row.get("CODTAB"))
return (CENTO - row.get_real("R0")).stringa(6,2);
}
}
return "0.00";
}
const char * TTrFa_mask::natura(const TString& codiva) const
{
const TRectype& ai = cache().get("%IVA", codiva);
TString & natura = get_tmp_string(4);
natura = ai.get("S12");
if(natura.blank())
{
const int tipo_iva11 = ai.get_int("S2");
const bool revcharge = tipo_iva11 >= 31 && tipo_iva11 <= 38;
const TString& tipo = ai.get("S1");
const int tipo_vendite = ai.get_int("S7");
// N1 escluse ex art 15
if (tipo_vendite == 5)
natura = "N5"; // regime del margine
else
if (revcharge)
natura = "N6"; // Inversione contabile (REVERSE CHARGE)
else
if (tipo == "NS")
natura = "N2"; // Non soggetto
else
if (tipo == "NI")
natura = "N3"; // Non imponibile
else
if (tipo == "ES")
natura = "N4"; // Esente
}
return natura;
}
real TTrFa_mask::get_IVA(const TString& codiva) const
{
const TRectype& ai = cache().get("%IVA", codiva);
return ai.get_real("R0");
}
char TTrFa_mask::revCharge(TString codcaus) const
{
const TRectype& caus = cache().get(LF_CAUSALI, codcaus);
const int reg_spec = caus.get_int(CAU_REGSPIVA);
if (reg_spec == 13 || reg_spec == 50 || reg_spec == 51) // reverse charge
return 'X';
return '\0';
}
/////////////////////////////////////////////////////////////////////////////////////
// TTrFa_app
/////////////////////////////////////////////////////////////////////////////////////
class TTrFa_app : public TSkeleton_application
{
TAnagrafica _ditta;
TString16 _cofi;
TFilename _dbname;
TLog_report* _log;
TString _logTFF;
enum { _spedita, _invio, _forzata, _numero, _numriga, _tipocf, _codcf, _ocfpi, _ragsoc, _rfso, _codnum, _numdoc, _datadoc,
_natura, _aliquota, _detraibile, _imponibile, _imposta, _reverse, _autofatt, _paiv, _codfis };
private:
int parse_line(const TString& line, TString& var, TString& val) const;
bool create_table(TScanner& TFF, const TString& table);
bool addDT(TString8& hfatt, TString20& bfatt, TToken_string* strarr);
bool setEsportato(TToken_string* strarr);
//const TRectype* find_parent_row(const TRectype& rdoc) const;
//int find_ancestors(const TRiga_documento& rdoc, TArray& ancestors) const;
protected:
//const char* descrizione(const TRiga_documento& rdoc) const;
//const TRectype& cco(const TRectype& doc) const; // Contratto/Convenzione/Offerta
void log(int severity, const char* msg);
bool show_log();
void set_IVA(const TString& codiva, TTrFa_record& TFF) const;
void set_IVA(const TRiga_documento& rdoc, TTrFa_record& TFF) const;
bool syncronizeDB();
bool createDB();
public:
virtual bool create();
virtual bool destroy();
virtual void main_loop();
bool send(TTrFa_mask* msk);
TTrFa_app() : _log(NULL) {}
};
void TTrFa_app::log(int severity, const char* msg)
{
if (severity < 0)
{
_logTFF = msg;
} else
if (_log == NULL)
{
_log = new TLog_report;
if (_logTFF.full())
{
TString txt;
txt << _logTFF << ": " << msg;
_log->log(severity, txt);
}
else
_log->log(severity, msg);
}
}
bool TTrFa_app::show_log()
{
bool ok = true;
if (_log)
{
_log->preview();
delete _log;
_log = NULL;
ok = noyes_box(TR("Si desidera procedere con la generazione file xml?"));
}
return ok;
}
void TTrFa_app::set_IVA(const TString& codiva, TTrFa_record& tff) const
{/*
const TRectype& ai = cache().get("%IVA", codiva);
const real aliquota = ai.get("R0");
tff.set("PI_ALIQUOTAIVA", aliquota);
if (codiva.full())
{
if (aliquota.is_zero())
tff.set("PI_NATURA", natura(codiva));
else
tff.set("PI_NATURA", "");
}*/
}
void TTrFa_app::set_IVA(const TRiga_documento& rdoc, TTrFa_record& tff) const
{/*
const TString8 codiva(rdoc.get(RDOC_CODIVA));
const TRectype& ai = cache().get("%IVA", codiva);
const real aliquota = ai.get("R0");
tff.set("PI_ALIQUOTAIVA", aliquota);
if (codiva.full())
{
if (aliquota.is_zero())
tff.set("PI_NATURA", natura(codiva));
else
tff.set("PI_NATURA", "");
}*/
}
int TTrFa_app::parse_line(const TString& line, TString& var, TString& val) const
{
if (line.blank())
return 0;
if (line[0] == '[')
{
var = line.mid(1);
var.rtrim(1);
val.cut(0);
return 1;
}
const int equal = line.find('=');
if (equal < 6)
return 0;
var = line.left(equal); var.trim();
val = line.mid(equal+1); val.trim();
return 2;
}
bool TTrFa_app::create_table(TScanner& tff, const TString& table)
{
TString query, var, val;
if (xvt_sql_table_exists(_db, table))
{
SLIST fields = xvt_sql_list_fields(_db, table);
while (!tff.eof())
{
const TString& line = tff.line();
const int n = parse_line(line, var, val);
if (n <= 0)
break;
if (var.starts_with("INDEX_"))
break;
if (xvt_slist_find_str(fields, var) == NULL)
{
query.cut(0) << "ALTER TABLE " << table << " ADD COLUMN " << var << ' ' << val << " NOT NULL";
if (val.find("INT") >= 0 || val.find("NUM") >= 0)
query << " DEFAULT 0";
else
query << " DEFAULT ''";
query << ";";
xvt_sql_execute(_db, query, NULL, NULL); // Create table
}
}
xvt_slist_destroy(fields);
}
else
{
query << "CREATE TABLE " << table << " (";
while (!tff.eof())
{
const TString& line = tff.line();
const int n = parse_line(line, var, val);
if (n <= 0)
break;
if (n == 1)
{
tff.push(line);
break;
}
if (var.starts_with("INDEX_"))
{
query.rtrim(1); // toglie ultima ,
query << ");";
xvt_sql_execute(_db, query, NULL, NULL); // Create table
query.cut(0);
query << "CREATE UNIQUE INDEX "
<< table << "_1 ON " << table
<< " (" << val << ");";
xvt_sql_execute(_db, query, NULL, NULL); // Create index
break;
}
else
{
query << "\n " << var << ' ' << val << " NOT NULL";
if (val.find("INT") >= 0 || val.find("NUM") >= 0)
query << " DEFAULT 0";
else
query << " DEFAULT ''";
query << ",";
}
}
}
return true;
}
bool TTrFa_app::create()
{
open_files(LF_MOV, LF_RMOV, LF_RMOVIVA, LF_TABCOM, LF_ANAG,
LF_CLIFO, LF_OCCAS, LF_CFVEN, LF_NDITTE, 0);
// Controllo preventivo dell'avvenuta conversione del tracciato record
TRectype rmoviva(LF_RMOVIVA);
//if (rmoviva.type("TFINVIO") != _boolfld)
//return error_box(TR("Database non convertito per il Trasferimento Elettronico"));
_ditta.init(LF_NDITTE, prefix().get_codditta());
_dbname = prefix().get_studio(); // base direcotry
_dbname.add("sql"); make_dir(_dbname);
TString16 d; d.format("TR%05ld.db", prefix().get_codditta());
_dbname.add(d);
bool create = !_dbname.exist();
_db = xvt_sql_open(_dbname, user(), "", _dbname.path());
if (_db == NULL)
return false;
if(create)
{
createDB();
}
return TSkeleton_application::create();
}
// Sincronizzo il DB SQL con quello di campo
bool TTrFa_app::syncronizeDB()
{
//xvt_sql_begin(_db);
return true;
}
bool TTrFa_app::createDB()
{
const TFilename ini = "tff.ini";
bool ok = ini.exist();
if (ok)
{
xvt_sql_begin(_db);
TScanner TFF(ini);
while (ok && !TFF.eof())
{
const TString& p = TFF.line();
if (p.starts_with("[TF") && p.ends_with("F]"))
{
TString16 table = p; table.strip("[]");
ok = create_table(TFF, table);
}
}
if (ok)
xvt_sql_commit(_db);
else
xvt_sql_rollback(_db);
}
else
return cantread_box(ini);
return true;
}
bool TTrFa_app::send(TTrFa_mask* msk)
{
// Mi carico i miei dati
TSheet_field& sheet = msk->sfield(F_RIGHE);
bool ok;
TProgress_monitor p(sheet.items(),"Esportazione fatture");
FOR_EACH_SHEET_ROW(sheet, r, strarr)
{
//if(!p.add_status())
//return false;
if(strcmp(strarr->get(_invio), "X") != 0) continue; // Non mi interessa se non <20> selezionata
TString8 hfatt; // Codice univoco di 6 caratteri dell'ufficio P.A.
TString20 bfatt; // Codice univoco di 20 caratteri del documento
hfatt.cut(0) << strarr->get(_tipocf) << "_" << strarr->get(_codcf);
bfatt.cut(0) << TDate(strarr->get(_datadoc)).year() << "|" << strarr->get(_codnum) << "|" << strarr->get(_numdoc) << "|" << strarr->get(_numriga);
log(-1, bfatt);
// Preparo l'esportazione
xvt_sql_begin(_db);
ok = addDT(hfatt, bfatt, strarr);
// Controllo in caso di errore
if(!ok)
{
log(-1, "WTF!?");
xvt_sql_rollback(_db);
//return false;
}
else
{
xvt_sql_commit(_db);
// Imposto l'esportazione
setEsportato(strarr);
}
}
return true;
}
bool TTrFa_app::addDT(TString8& hfatt, TString20& bfatt, TToken_string* strarr)
{
const char* const paese = "IT";
static const TFirm& firm = prefix().firm();
// <DatiTrassmissione>
TTrFa_record tff0100f("TFF0100F");
tff0100f.set("P1_KEYHEADERFATT", hfatt);
tff0100f.set("P1_KEYBODYFATT", bfatt);
tff0100f.set("P1_TRASMITTPAESE", paese);
tff0100f.set("P1_TRASMITTCOD", _cofi);
tff0100f.set("P1_PRGINVIO", ""); // Ci pensa SiAggTF
tff0100f.set("P1_CODDEST", hfatt);
TString80 tel; tel << firm.get(NDT_PTEL) << firm.get(NDT_TEL);
tff0100f.set("P1_TELEFONO", tel);
tff0100f.set("P1_MAIL", firm.get(NDT_MAIL));
tff0100f.set("P1_GESTIONE", "D");
tff0100f.insert();
// </DatiTrassmissione>
/********************************************************************************************************************
* CessionarioCommit *
********************************************************************************************************************/
// <CedentePrestatore>
TTrFa_record tff0200f("TFF0200F");
tff0200f.set("P2_KEYHEADERFATT", hfatt);
tff0200f.set("P2_KEYBODYFATT", bfatt);
tff0200f.remove();
if (_ditta.partita_IVA().full())
{
tff0200f.set("P2_FISCIVAPAESE", paese); // Sempre IT
tff0200f.set("P2_FISCIVACOD", _ditta.partita_IVA());
}
tff0200f.set("P2_CODFISCALE", _ditta.codice_fiscale());
if (_ditta.fisica())
{
tff0200f.set("P2_ANANOME", _ditta.nome());
tff0200f.set("P2_ANACOGNOME", _ditta.cognome());
}
else
{
tff0200f.set("P2_ANADENOMIN", _ditta.ragione_sociale());
}
// DatiSede
tff0200f.set("P2_SEDEIND", _ditta.via_residenza());
tff0200f.set("P2_SEDENRCIVICO", _ditta.civico_residenza());
tff0200f.set("P2_SEDECAP", _ditta.CAP_residenza());
tff0200f.set("P2_SEDECOMUNE", _ditta.comune_residenza());
tff0200f.set("P2_SEDEPROV", _ditta.provincia_residenza());
tff0200f.set("P2_SEDENAZ", paese);
TString myrfso;
if(haveRFSO(myrfso))
{
TRectype r_ana = cache().get(LF_ANAG, TString(myrfso[0]) << myrfso.sub(1));
if(r_ana.get_char("TIPORFSO") == 'S') // Stabile Organizzazione
{
tff0200f.set("P2_STABORGIND", r_ana.get("INDRES"));
tff0200f.set("P2_STABORGNRCIVICO", r_ana.get("CIVRES"));
tff0200f.set("P2_STABORGCAP", r_ana.get("CAPRES"));
TRectype r_comune = cache().get(LF_COMUNI, TString("|") << r_ana.get("COMRES"));
tff0200f.set("P2_STABORGCOMUNE", r_comune.get("DENCOM"));
tff0200f.set("P2_STABORGPROV", r_comune.get("PROVCOM"));
tff0200f.set("P2_STABORGNAZ", "IT");
}
else // Rappresentante Fiscale
{
TTrFa_record tff0300f("TFF0300F");
tff0300f.set("P3_KEYHEADERFATT", hfatt);
tff0300f.set("P3_KEYBODYFATT", bfatt);
tff0300f.remove();
tff0300f.set("P3_FISCIVAPAESE", paese); // Io italiano posso avere un rappresentante fiscale italiano
tff0300f.set("P3_FISCIVACODICE", r_ana.get("PAIV"));
if(r_ana.get_char("TIPOA") == 'G')
{
tff0300f.set("P3_ANADENOMI", r_ana.get("RAGSOC"));
}
else
{
TString nomCom = r_ana.get("RAGSOC");
tff0300f.set("P3_ANANOME", nomCom.sub(30));
tff0300f.set("P3_ANACOGNOME", nomCom.sub(0,30));
}
tff0300f.set("P3_GESTIONE", "D");
tff0300f.insert();
}
}
tff0200f.set("P2_GESTIONE", "D");
tff0200f.insert();
/********************************************************************************************************************
* Cedeprest *
********************************************************************************************************************/
// </CedentePrestatore>
TAnagrafica cedeprest(LF_CLIFO, strarr->get_char(_tipocf), strarr->get_long(_codcf));
TRectype r_cedeprest = cache().get(LF_CLIFO, TString(strarr->get_char(_tipocf)) << "|" << strarr->get_long(_codcf));
TString statocli = cache().get("%STA", r_cedeprest.get("STATOCF"), "S2");
// <CessionarioCommittente>
TTrFa_record tff0400f("TFF0400F");
tff0400f.set("P4_KEYHEADERFATT", hfatt);
tff0400f.set("P4_KEYBODYFATT", bfatt);
tff0400f.remove();
// Autofattura
if(strcmp(strarr->get(_autofatt),"X") == 0)
{
tff0400f.set("P4_FISCIVAPAESE", paese);
tff0400f.set("P4_FISCIVACOD", _ditta.partita_IVA());
tff0400f.set("P4_CODFISC", _ditta.codice_fiscale());
}
else // Fattura normale
{
if (cedeprest.stato_partita_IVA().full() && cedeprest.partita_IVA().full())
{
tff0400f.set("P4_FISCIVAPAESE", cedeprest.stato_partita_IVA());
tff0400f.set("P4_FISCIVACOD", cedeprest.partita_IVA());
}
else
tff0400f.set("P4_CODFISC", cedeprest.codice_fiscale());
}
if (cedeprest.fisica())
{
tff0400f.set("P4_ANANOME", cedeprest.nome());
tff0400f.set("P4_ANACOGNOME", cedeprest.cognome());
}
else
{
tff0400f.set("P4_ANADENOM", cedeprest.ragione_sociale());
}
// DatiSede
tff0400f.set("P4_SEDEIND", cedeprest.via_residenza());
tff0400f.set("P4_SEDENRCIVICO", cedeprest.civico_residenza());
tff0400f.set("P4_SEDECAP", cedeprest.CAP_residenza());
tff0400f.set("P4_SEDECOMUNE", cedeprest.comune_residenza());
tff0400f.set("P4_SEDEPROV", cedeprest.provincia_residenza());
tff0400f.set("P4_SEDENAZ", statocli);
tff0400f.set("P4_GESTIONE", "D");
tff0400f.insert();
TString rfso;
if(hasRFSO(strarr->get_char(_tipocf), strarr->get_long(_codcf), rfso))
{
TRectype r_ana = cache().get(LF_ANAG, TString(myrfso[0]) << rfso.sub(1));
TTrFa_record tff3100f("TFF3100F");
tff3100f.set("PH_KEYHEADERFATT", hfatt);
tff3100f.set("PH_KEYBODYFATT", bfatt);
tff3100f.remove();
if(r_ana.get_char("TIPORFSO") == 'S') // Stabile Organizzazione
{
tff3100f.set("PH_STABORGIND", r_ana.get("INDRES"));
tff3100f.set("PH_STABORGNRCIVICO", r_ana.get("CIVRES"));
tff3100f.set("PH_STABORGCAP", r_ana.get("CAPRES"));
TRectype r_comune = cache().get(LF_COMUNI, TString("|") << r_ana.get("COMRES"));
tff3100f.set("PH_STABORGCOMUNE", r_comune.get("DENCOM"));
tff3100f.set("PH_STABORGPROV", r_comune.get("PROVCOM"));
tff3100f.set("PH_STABORGNAZ", "IT");
}
else // Rappresentante Fiscale
{
// La P.IVA del rappresentante fiscale deve essere in AT quindi non faccio alcun controllo,
// se il valore nullo perch<63> Extra CEE non <20> un errore di campo (Anche perch<63> che senso ha un RF extra CEE?)
tff3100f.set("PH_FISCIVAPAESE", r_ana.get("STATOPAIV") == "" ? "IT" : r_ana.get("STATOPAIV"));
tff3100f.set("PH_FISCIVACODICE", r_ana.get("PAIV"));
if(r_ana.get_char("TIPOA") == 'G')
{
tff3100f.set("PH_ANADENOMI", r_ana.get("RAGSOC"));
}
else
{
TString nomCom = r_ana.get("RAGSOC");
tff3100f.set("PH_ANANOME", nomCom.sub(30));
tff3100f.set("PH_ANACOGNOME", nomCom.sub(0,30));
}
}
tff3100f.set("PH_GESTIONE", "D");
tff3100f.insert();
}
// </CessionarioCommittente>
/********************************************************************************************************************
* Fattura *
********************************************************************************************************************/
// <DatiFatturaBody>
TTrFa_record tff0700f("TFF0700F");
tff0700f.set("P7_KEYHEADERFATT", hfatt);
tff0700f.set("P7_KEYBODYFATT", bfatt);
tff0700f.remove();
tff0700f.set("P7_GESTIONE", "D");
tff0700f.insert();
TTrFa_record tff2200f("TFF2200F");
tff2200f.set("PL_KEYHEADERFATT", hfatt);
tff2200f.set("PL_KEYBODYFATT", bfatt);
tff2200f.remove();
tff2200f.set("PL_IMPONIBILE", strarr->get(_imponibile));
tff2200f.set("PL_IMPOSTA", strarr->get(_imposta));
tff2200f.set("PL_ALIQUOTAIVA", strarr->get(_aliquota));
tff2200f.set("PL_NATURA", strarr->get(_natura));
if(strcmp(strarr->get(_detraibile), "0.00") != 0)
{
tff2200f.set("PL_DETRAIBILE", strarr->get(_detraibile));
}
else if(false) // Sempre disabilitato!
{
tff2200f.set("PL_DEDUCIBILE", "SI");
}
tff2200f.set("PL_GESTIONE", "D");
tff2200f.insert();
return true;
}
bool TTrFa_app::setEsportato(TToken_string* strarr)
{
if(strcmp(strarr->get(_forzata), "X") == 0)
{
// Devo inserire la riga in trasfatt
TRectype row(LF_TRASFATT);
if(strcmp(strarr->get(_spedita), "X") == 0) // Controllo che non sia gi<67> stata spedita prima
{
row = cache().get(LF_TRASFATT, TString(strarr->get(_numero))<<"|"<<(strarr->get(_numriga)));
}
else
{
row.put("NUMREG", strarr->get(_numero));
row.put("NUMRIG", strarr->get(_numriga));
}
row.put("TIPODOC", strarr->get(_codnum));
row.put("NDOC", strarr->get(_numdoc));
row.put("DATADOC", strarr->get(_datadoc));
row.put("IMPONIBILE", strarr->get(_imponibile));
row.put("IMPOSTA", strarr->get(_imposta));
row.put("REVCHARGE", strarr->get(_reverse));
row.put("AUTOFATT", strarr->get(_autofatt));
row.put("TFINVIO", "X");
row.put("TFDATA", TDate(TODAY));
return row.write_rewrite(TLocalisamfile(LF_TRASFATT)) == NOERR;
}
else
{
TRectype row = cache().get(LF_RMOVIVA, TString(strarr->get(_numero))<<"|"<<(strarr->get(_numriga)));
row.put("TFINVIO", "X");
row.put("TFDATA", TDate(TODAY));
return row.rewrite(TLocalisamfile(LF_RMOVIVA)) == NOERR;
}
}
void TTrFa_app::main_loop()
{
TTrFa_mask msk("tf0100a");
while (msk.run() == K_ENTER)
{
TSheet_field& sheet = msk.sfield(F_RIGHE);
TString msg("La tabella dei movimenti <20> vuota, vuoi caricarla con i filtri selezionati?");
if(sheet.empty() && yesno_box(msg))
{
msk.load_sheet();
}
send(&msk);
}
}
bool TTrFa_app::destroy()
{
if (_cofi.full())
ini_set_string(CONFIG_DITTA, "pa", "TRASMITTCOD", _cofi);
xvt_sql_close(_db); _db = NULL;
return TSkeleton_application::destroy();
}
int tf0100(int argc, char* argv[])
{
TTrFa_app t2t;
t2t.run(argc, argv, TR("Trasferimento Fatture Elettroniche"));
return 0;
}