#include #include #include #include #include #include #include #include #include #include #include "../ve/velib05.h" #include "../cg/cglib03.h" #include "tf0.h" #include "tf0100a.h" #include "../fe/felib.h" #include #include #include #include #include #include #include #include ///////////////////////////////////////////////////////////////////////////////////// // 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à eseguito max 2 volte if (!c_mov->next_match(0)) return eofm; rigaMov = c_mov->curr(); } // Mi muovo finchè 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 è diventato valido if(al.empty()) al = TODAY; filter << "&&(BETWEEN(DATADOC," << dal.date2ansi() << ',' << al.date2ansi() << "))"; if(tipocf != 'T') { movKey = 3; filMov.put("TIPO", tipocf); // Se è 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!=\"\"")<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à 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 è già 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à 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 è 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à, così 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 è 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(); // 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(); // /******************************************************************************************************************** * CessionarioCommit * ********************************************************************************************************************/ // 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 * ********************************************************************************************************************/ // 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"); // 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è Extra CEE non è un errore di campo (Anche perchè 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(); } // /******************************************************************************************************************** * Fattura * ********************************************************************************************************************/ // 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à 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 è 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; }