#include #include #include #include #include #include #include #include #include #include #include #include #include "../fe/felib.h" #include "ef0301.h" #include "ef1100a.h" #include "clifo.h" ///////////////////////////////////////////////////////////////////////////////////// // Globals ///////////////////////////////////////////////////////////////////////////////////// static XVT_SQLDB _db = NULL; // SEPA sqlite db ///////////////////////////////////////////////////////////////////////////////////// // 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; } // Aggiorna il file dst se pił vecchio di src (Potrebbe diventare una funzione di XVT.h) bool xvt_fsys_fupdate(const char* src, const char* dst) { bool ok = false; if (xvt_fsys_file_exists(src)) { const long tsrc = xvt_fsys_file_attr(src, XVT_FILE_ATTR_MTIME); if (tsrc > 0) { long tdst = 0; if (xvt_fsys_file_exists(dst)) tdst = xvt_fsys_file_attr(dst, XVT_FILE_ATTR_MTIME); if (tsrc > tdst) ok = xvt_fsys_fcopy(src, dst) != 0; } } return ok; } ///////////////////////////////////////////////////////////////////////////////////// // TJava_profile ///////////////////////////////////////////////////////////////////////////////////// class TJava_profile : public TObject { TFilename _path; TString_array _row; bool _dirty; protected: const TString& path2prop(const char* path) const; public: void set(const char* key, const char* value); void save(); TJava_profile(const char* path); ~TJava_profile() { if (_dirty) save(); } }; // Converte una stringa in un percorso per un file profile di Java const TString& TJava_profile::path2prop(const char* path) const { TFilename percorso; for (const char* c = path; *c; c++) { if (*c == ':' || is_slash(*c)) percorso << '\\'; percorso << *c; } return get_tmp_string() = percorso; } void TJava_profile::set(const char* key, const char* value) { _dirty = true; FOR_EACH_ARRAY_ROW(_row, r, line) { if (line->starts_with(key, true)) { const int equal = line->find('='); if (equal > 0) { line->cut(equal + 1); *line << path2prop(value); return; } } } TToken_string* prop = new TToken_string(50, '='); prop->add(key); prop->add(path2prop(value)); _row.add(prop); } void TJava_profile::save() { ofstream out(_path); if (out.good()) { FOR_EACH_ARRAY_ROW(_row, r, line) if (line->full()) out << *line << endl; _dirty = false; } else cantwrite_box(_path); } TJava_profile::TJava_profile(const char* path) : _path(path), _dirty(false) { TScanner s(_path); while (!s.eof()) { const TString& line = s.line(); if (line.full()) _row.add(new TToken_string(line, '=')); else break; } } ///////////////////////////////////////////////////////////////////////////////////// // TSEPA_record ///////////////////////////////////////////////////////////////////////////////////// // Contenitore di campi di un record di database SQLite class TSEPA_record : public TObject { TString8 _table; TToken_string _key; TAssoc_array _fields; protected: void copy(const TSEPA_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 TSEPA_record(*this); } virtual bool ok() const { return _table.not_empty(); } TSEPA_record& operator=(const TSEPA_record& rec) { copy(rec); return *this; } TSEPA_record(const TSEPA_record& rec) { copy(rec); } TSEPA_record(const char* table); }; // Imposta il valore di un campo variant void TSEPA_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 TSEPA_record::set(const char* fld, long val) { const TVariant var(val); set(fld, var); } // Imposta il valore di un campo stringa void TSEPA_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 TSEPA_record::set(const char* fld, const TString& val) { const TVariant var(val); set(fld, var); } // Imposta il valore di un campo numerico void TSEPA_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 TSEPA_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 TSEPA_record::set(const char* fld, bool var) { set(fld, var ? "TRUE" : "FALSE"); } // Legge il valore di un campo variant const TVariant& TSEPA_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& TSEPA_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().date2ansi(); // Data ANSI = YYYYMMDD 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 TSEPA_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 db_search_record(void* jolly, int cols, char** values, char** names) { TSEPA_record& rec = *(TSEPA_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 TSEPA_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, db_search_record, this) == 1; } // Carica un record in base ad un massimo di 3 campi chiave bool TSEPA_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 TSEPA_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 TSEPA_record::TSEPA_record(const char* table) : _table(table), _key(15, ',') { _key = ini_get_string("./sepa.ini", table, "INDEX_1"); if (_key.empty()) { // Cerco di costruire i nomi della chiave cercando la K, come in P1_KEYHEADERFATT TConfig cfg("sepa.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); } ///////////////////////////////////////////////////////////////////////////////////// // TSEPA_mask ///////////////////////////////////////////////////////////////////////////////////// class TSEPA_mask : public TAutomask { TAssoc_array _banche; protected: void append(TSheet_field& s, TToken_string& row, TISAM_recordset& dist, const char* fld); void fill_sheet(); protected: virtual bool on_field_event(TOperable_field& o, TField_event fe, long jolly); const TString& banca(const char* abi, const char* cab); public: TSEPA_mask() : TAutomask("ef1100a") {} }; const TString& TSEPA_mask::banca(const char* abi, const char* cab) { TString16 cod; cod << abi << cab; TString* desc = (TString*)_banche.objptr(cod); if (desc == NULL) { desc = new TString; _banche.add(cod, desc); TString16 ban; ban << abi; TString dabi = cache().get("%BAN", ban, "S0"); ban << cab; TString dcab = cache().get("%BAN", ban, "S0"); if (dcab.starts_with(dabi)) *desc = dcab; else *desc << dabi << ' ' << dcab; } return *desc; } void TSEPA_mask::fill_sheet() { TSheet_field& s = sfield(F_SHEET); s.hide(); s.destroy(); TString query; query << "USE EFFETTI KEY 3" << "\nSELECT (TIPOCF=#TIPOCF)&&(TIPOPAG=#TIPOPAG)&&(NRIGADIST=1)"; if (!get_bool(F_SHOWALL)) query << "&&(EFFCOMP!=\"X\")"; if (!field(F_ABI).empty()) { query << "&&(STR("; query << "(CODABIP=#ABI)"; if (!field(F_CAB).empty()) query << "&&(CODCABP=#CAB)"; query << "))"; } query << "\nFROM DATASCAD=#DATA"; TISAM_recordset dist(query); dist.set_var("#TIPOCF", get(F_TIPOCF)); dist.set_var("#TIPOPAG", get(F_TIPO)); dist.set_var("#DATA", get(F_DATA)); dist.set_var("#ABI", get(F_ABI)); dist.set_var("#CAB", get(F_CAB)); TProgress_monitor pi(dist.items(), TR("Selezione disitinte")); const TRelation& rel = *dist.cursor()->relation(); const TRectype& curr = rel.curr(); for (bool ok = dist.move_first(); ok; ok = dist.move_next()) { TToken_string& row = s.row(-1); FOR_EACH_MASK_FIELD(s.sheet_mask(), i, f) if (f->field() != NULL) { const char* val = f->field()->read(curr); row.add(val, s.cid2index(f->dlg())); } TDistinta dist(curr); real importo; FOR_EACH_ARRAY_ITEM(dist.righe(), i, obj) { const TRectype& eff = *(const TRectype*)obj; importo += eff.get_real(EFF_IMPORTO); } row.add(importo.string(), s.cid2index(S_IMPORTO)); const TString& desc = banca(curr.get(EFF_CODABIP), curr.get(EFF_CODCABP)); row.add(desc, s.cid2index(S_BANCA)); row.add(curr.get(EFF_EFFCONT)); row.add(curr.get(EFF_EFFCOMP)); if (!pi.add_status()) break; } s.force_update(); s.show(); } bool TSEPA_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) { switch (o.dlg()) { case F_DATA: if (e == fe_init) { TDate d(TODAY); d.set_day(1); d.addmonth(-3); o.set(d.string()); } case F_TIPO: case F_TIPOCF: case F_CAB: case F_SHOWALL: if (e == fe_modify && is_running()) fill_sheet(); break; case F_SHEET: if (e == fe_init) fill_sheet(); if (e == se_query_add || e == se_query_del) return false; break; default: break; } return true; } ///////////////////////////////////////////////////////////////////////////////////// // TSEPA_app ///////////////////////////////////////////////////////////////////////////////////// class TSEPA_app : public TSkeleton_application { TAnagrafica _ditta; TString16 _cofi, _cuc; TFilename _dbname, _dbon, _drid; TLog_report* _log; TString _logpaf; protected: int parse_line(const TString& line, TString& var, TString& val) const; bool create_table(TScanner& paf, const TString& table); TDate data_scadenza(const TRectype& eff) const; real genera_BON(const TString& PmtInfId, const TEffetto& eff); bool genera_dist_BON(char td, long nd, TLog_report& log); bool genera_SEPA_BON(const TMask& m, TLog_report& log); real genera_RID(const TString& PmtInfId, const TEffetto& eff, TLog_report& log); bool genera_dist_RID(char td, long nd, TLog_report& log); bool genera_SEPA_RID(const TMask& m, TLog_report& log); bool convert_apos(const TString& xml) const; bool genera_xml(int tipo, bool tuttapost); void completa(TDistinta& dist) const; public: virtual bool create(); virtual void main_loop(); virtual bool destroy(); }; int TSEPA_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 TSEPA_app::create_table(TScanner& paf, const TString& table) { TString query, var, val; if (xvt_sql_table_exists(_db, table)) { SLIST fields = xvt_sql_list_fields(_db, table); while (!paf.eof()) { const TString& line = paf.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 (!paf.eof()) { const TString& line = paf.line(); const int n = parse_line(line, var, val); if (n <= 0) break; if (n == 1) { paf.push(line); break; } if (var.starts_with("INDEX_")) { query.rtrim(1); // toglie ultima , query << ");"; xvt_sql_execute(_db, query, NULL, NULL); // Create table if (val.full()) { 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 TSEPA_app::create() { if (!has_module(SEAUT)) return error_box(TR("Si richiede l'attivazione del modulo SEPA")); const long cd = prefix().get_codditta(); if (cd <= 0 || !prefix().is_firm()) return cantread_box(TR("Ditta corrente")); open_files(LF_TAB, LF_TABCOM, LF_TABMOD, LF_ANAG, LF_CLIFO, LF_CFVEN, LF_CFBAN, LF_NDITTE, LF_EFFETTI, LF_REFFETTI, 0); _ditta.init(LF_NDITTE, cd); // Crea cartelle di lavoro: C:\CAMPO\DATI\SEPA\BON e C:\CAMPO\DATI\\SEPA\RID _dbname = firm2dir(cd); _dbname.rtrim(6); _dbname.add("SEPA"); make_dir(_dbname); _dbon = _dbname; _dbon.add("BON"); make_dir(_dbon); _drid = _dbname; _drid.add("RID"); make_dir(_drid); // Crea file di lavoro: C:\CAMPO\DATI\00001A\SEPA\SEPA.db TString16 name; name.format("SEPA%05d.db", cd); _dbname.add(name); _db = xvt_sql_open(_dbname, user(), "", _dbname.path()); if (_db == NULL) return cantread_box(_dbname); const TFilename ini = "sepa.ini"; bool ok = ini.exist(); if (ok) { xvt_sql_begin(_db); TScanner sepa(ini); while (ok && !sepa.eof()) { const TString& p = sepa.line(); if (p.starts_with("[SE") && p.ends_with("00F]")) { TString16 table = p; table.strip("[]"); ok = create_table(sepa, table); } } if (ok) xvt_sql_commit(_db); else xvt_sql_rollback(_db); } else return cantread_box(ini); _cofi = ini_get_string(CONFIG_DITTA, "ef", "TRASMITTCOD"); if (_cofi.blank()) _cofi = _ditta.codice_fiscale(); _cuc = ini_get_string(CONFIG_DITTA, "ef", "CUC"); return ok && TSkeleton_application::create(); } bool TSEPA_app::destroy() { if (_cofi.full()) ini_set_string(CONFIG_DITTA, "ef", "TRASMITTCOD", _cofi); xvt_sql_close(_db); _db = NULL; return TSkeleton_application::destroy(); } // Id univoco distinta static bool SEPA_MsgId(const TRectype& dist, TString& id) { const long firm = prefix().get_codditta(); const char tipo = dist.get_char(EFF_TIPODIST); const long num = dist.get_long(EFF_NDIST); const int abi = dist.get_int(EFF_CODABIP); const int cab = dist.get_int(EFF_CODCABP); id.format("%05ld-%c%05d-%05d%05d", firm, tipo, num, abi, cab); CHECKS(id.len() <= 26, "MsgId pił lungo di 26 caratteri ", id); return abi > 0 && cab > 0; } // Instruction Identification static bool SEPA_InstrId(const TRectype& eff, TString& id) { const char tipo = eff.get_char(EFF_TIPODIST); const long num = eff.get_long(EFF_NDIST); const int nriga = eff.get_int(EFF_NRIGADIST); id.format("%c%05ld-%05d", tipo, num, nriga); return nriga > 0; } static void AS400_date_time(TString& d, TString& t) { const struct tm& lt = *xvt_time_now(); d.format("%04d%02d%02d", lt.tm_year+1900, lt.tm_mon, lt.tm_mday); t.format("%02d%02d%02d", lt.tm_hour, lt.tm_min, lt.tm_sec); } static const char* tipo_sequenza(const TRectype& eff) { const TRectype& mandato = cache().get("&MAN", eff.get(EFF_MANDATO)); if (!mandato.empty()) { const TString& s = mandato.get("S6"); if (s.full()) return s; } return "RCUR"; } TDate TSEPA_app::data_scadenza(const TRectype& eff) const { const TDate oggi(TODAY); TDate dt = eff.get(EFF_DSCVAL); if (!dt.ok() || dt < oggi) dt = eff.get_date(EFF_DATASCAD); if (!dt.ok() || dt < oggi) { dt = oggi; // dt.set_end_month(); // Forse per RID } return dt; } real TSEPA_app::genera_BON(const TString& PmtInfId, const TEffetto& eff) { TSEPA_record body("SEBOD00F"); TString instr_id; SEPA_InstrId(eff, instr_id); body.set("SED001", PmtInfId); body.set("SED002", instr_id); body.set("SED003", "SUPP"); body.set("SED004", ""); // Prtry body.set("SED005", "EUR"); const real imp = eff.get_real(EFF_IMPORTO); body.set("SED006", imp); const TAnagrafica creditor(eff); body.set("SED009", creditor.ragione_sociale()); body.set("SED010", "ADDR"); // Tipo indirizzo 2.6.2.1 //body.set("SET011", divisione); //body.set("SET012", sotto-divisione); body.set("SED013", creditor.via_residenza()); body.set("SED014", creditor.civico_residenza()); body.set("SED015", creditor.CAP_residenza()); body.set("SED016", creditor.comune_residenza()); body.set("SED017", creditor.provincia_residenza()); body.set("SED018", creditor.stato_partita_IVA()); body.set("SED019", creditor.partita_IVA().full() ? creditor.partita_IVA() : creditor.codice_fiscale()); body.set("SED020", "ADE"); body.set("SED021", creditor.stato_partita_IVA()); body.set("SED022", eff.get(EFF_IBAN)); body.insert(); TString16 codbnp; codbnp << eff.get(EFF_CODABIP) << eff.get(EFF_CODCABP) << eff.get(EFF_PROGBNP); const TRectype& bnp = cache().get("BNP", codbnp); const bool sepa_dett = bnp.get_bool("B3"); // Dettagli fatture pagate const TRecord_array reff(eff.get(EFF_NPROGTR), LF_REFFETTI); for (int r = reff.first_row(); r > 0 && r <= reff.last_row(); r = reff.succ_row(r)) { const TRectype& riga = reff[r]; TSEPA_record rata("SEBOF00F"); rata.set("SEF001", PmtInfId); rata.set("SEF002", instr_id); rata.set("SEF003", (long)r); rata.set("SEF004", "CINV"); // Commercial Invoice (oppure DEBN, CREN?) TString doc_id; doc_id << riga.get(REFF_ANNO) << '/' << riga.get(REFF_NFATT); doc_id.strip_spaces(); const TString& cup = eff.get(EFF_CUP); if (cup.full()) doc_id << " CUP-" << cup; else { const TString& cig = eff.get(EFF_CIG); if (cig.full()) doc_id << " CIG-" << cig; } rata.set("SEF007", doc_id); rata.set("SEF008", riga.get_date(REFF_DATAFATT)); rata.set("SEF009", riga.get_real(REFF_IMPFATT)); rata.set("SEF011", riga.get_real(REFF_IMPORTO)); if (!sepa_dett) // NON STRUTTURATO { TString desc; const TCurrency tot = riga.get_real(REFF_IMPFATT); const TCurrency pag = riga.get_real(REFF_IMPORTO); if (tot > pag) { desc.format(FR("Fatt:%s Data %s Tot:%s Pag:%s"), (const char*)doc_id, (const char*)riga.get(REFF_DATAFATT), tot.string(), pag.string()); } else { desc.format(FR("Fatt:%s Data:%s Imp:%s"), (const char*)doc_id, (const char*)riga.get(REFF_DATAFATT), pag.string()); } if (r > 1) desc.insert(" - "); rata.set("SEF012", desc); } rata.insert(); } return imp; } void TSEPA_app::completa(TDistinta& dist) const { TLocalisamfile eff(LF_EFFETTI); for (int i = 0; i < dist.items(); i++) { TEffetto& r = dist[i]; r.put(EFF_EFFCOMP, true); r.rewrite(eff); } } bool TSEPA_app::genera_dist_BON(char td, long nd, TLog_report& log) { TDistinta dist(td, nd); TAssoc_array subdist; for (int i = 0; i < dist.items(); i++) { const TEffetto& eff = dist[i]; const char* ts = tipo_sequenza(eff); if (ts && *ts) { const TDate data = data_scadenza(eff); TString16 key; key.format("%s_%ld", ts, data.date2ansi()); TArray* list = (TArray*)subdist.objptr(key); if (list == NULL) { list = new TArray; subdist.add(key, list); } list->add(eff); } else { TString msg; msg << TR("Tipo sequenza non valido alla riga ") << eff.get(EFF_NRIGADIST) << TR(" della distinta ") << nd; log.log(2, msg); } } if (subdist.empty()) return false; // Identificativo univoco messaggio TString msg_id; SEPA_MsgId(dist[0], msg_id); TString8 msg_date, msg_time; AS400_date_time(msg_date, msg_time); // In AS400 non si gestiscono le sottodistinte, per cui creo una distinta per ogni sottodisitinta FOR_EACH_ASSOC_OBJECT(subdist, hobj, key, itm) { const TArray& trans = objptr2array(itm); TSEPA_record head("SEBOT00F"); // Il codice univoco diventa MsgId + Id TString PmtInfId; PmtInfId << msg_id << '-' << msg_date.left(8); log.log(0, PmtInfId); head.set("SET001", PmtInfId); // MsgId // Nel file XML viene richiesta data/ora in formato ISO, qui invece ci sono due campi distinti ą la AS400 head.set("SET002", msg_date); // CreDtTm data head.set("SET0021", msg_time); // CreDtTm ora head.set("SET003", long(trans.items())); // NbOfTxs head.set("SET004", ZERO); // totale head.set("SET005", _ditta.ragione_sociale()); // Nm const TEffetto& e0 = dist[0]; TString16 codtab = e0.get(EFF_CODABIP); codtab << e0.get(EFF_CODCABP); TString4 prog = e0.get(EFF_PROGBNP); if (prog.full()) codtab << prog; const TRectype& bnp = cache().get("BNP", codtab); TString8 cuc = bnp.get("S5"); // CUC specifico per banca di presentazione if (cuc.blank()) cuc = _cuc; // CUC generale della ditta corrente head.set("SET006", cuc); // Id head.set("SET007", "CBI"); // Issr head.set("SET008", "TRF"); // TRF,CHK,TRA head.set("SET009", "NORM"); // Priority2Code: NORM o HIGH const TDate ReqdExctnDt = data_scadenza(e0); head.set("SET010", ReqdExctnDt); // Data richiesta dal mittente YYYY-MM-DD // 2.6.1 AT-02 Name of the originator head.set("SET011", _ditta.ragione_sociale()); // Cdtr head.set("SET012", "ADDR"); // AddressType2Code 2.6.2.1 //head.set("SET013", divisione); //head.set("SET014", sotto-divisione); head.set("SET015", _ditta.via_residenza()); head.set("SET016", _ditta.civico_residenza()); head.set("SET017", _ditta.CAP_residenza()); head.set("SET018", _ditta.comune_residenza()); head.set("SET019", _ditta.provincia_residenza()); head.set("SET020", _ditta.stato_partita_IVA()); // Alpha-2 code head.set("SET021", _ditta.partita_IVA()); head.set("SET022", "ADE"); head.set("SET023", _ditta.stato_partita_IVA()); head.set("SET024", bnp.get("S3")); //head.set("SET025", BIC); ??? head.set("SET026", codtab.left(5)); head.set("SET027", "SLEV"); real totale; FOR_EACH_ARRAY_ITEM(trans, r, peff) { const TEffetto& e = *(TEffetto*)peff; totale += genera_BON(PmtInfId, e); } head.set("SET004", totale); head.insert(); } completa(dist); return true; } bool TSEPA_app::genera_SEPA_BON(const TMask& msk, TLog_report& log) { xvt_sql_begin(_db); TString query; query = "DELETE FROM SEBOT00F;"; xvt_sql_execute(_db, query, NULL, 0L); query = "DELETE FROM SEBOD00F;"; xvt_sql_execute(_db, query, NULL, 0L); query = "DELETE FROM SEBOF00F;"; xvt_sql_execute(_db, query, NULL, 0L); const TSheet_field& sht = msk.sfield(F_SHEET); TRecnotype nd = 0; FOR_EACH_SHEET_ROW(sht, r, row) if (row->get_char(0) == 'X') { char tipo = '\0'; row->get(1, tipo); long numd = 0L; row->get(2, numd); if (genera_dist_BON(tipo, numd, log)) nd++; } if (nd > 0) xvt_sql_commit(_db); else xvt_sql_rollback(_db); return nd > 0; } real TSEPA_app::genera_RID(const TString& PmtInfId, const TEffetto& eff, TLog_report& log) { TSEPA_record body("SERID00F"); TString instr_id; SEPA_InstrId(eff, instr_id); body.set("SERD01", PmtInfId); body.set("SERD02", instr_id); body.set("SERD03", "SUPP"); body.set("SERD04", "EUR"); const real imp = eff.get_real(EFF_IMPORTO); body.set("SERD05", imp); body.set("SERD06", "SLEV"); TString80 cmndt = eff.get(EFF_MANDATO); TDate dmndt = cache().get("&MAN", cmndt, "D0"); if (cmndt.blank()) { TString msg; msg << TR("Codice mandato assente in ") << instr_id; log.log(1, msg); cmndt = instr_id; dmndt = eff.get_date(EFF_DATADIST); } body.set("SERD07", cmndt); // Codice mandato body.set("SERD08", dmndt); // Data mandato body.set("SERD09", "0"); // era FALSE e non funzionava const TAnagrafica debitor(eff); body.set("SERD11", debitor.ragione_sociale()); body.set("SERD121", "ADDR"); // Tipo indirizzo 2.6.2.1 //body.set("SERD12", divisione); //body.set("SERD13", sotto-divisione); body.set("SERD14", debitor.via_residenza()); body.set("SERD15", debitor.civico_residenza()); body.set("SERD16", debitor.CAP_residenza()); body.set("SERD17", debitor.comune_residenza()); body.set("SERD18", debitor.provincia_residenza()); body.set("SERD19", debitor.stato_partita_IVA()); body.set("SERD20", debitor.fisica() ? 1L : 0L); body.set("SERD22", debitor.partita_IVA().full() ? debitor.partita_IVA() : debitor.codice_fiscale()); body.set("SERD23", "ADE"); body.set("SERD24", debitor.stato_partita_IVA()); body.set("SERD25", eff.get(EFF_IBAN)); body.set("SERD26", "SUPP"); body.set("SERD27", "CRED"); body.insert(); TString16 codbnp; codbnp << eff.get(EFF_CODABIP) << eff.get(EFF_CODCABP) << eff.get(EFF_PROGBNP); const TRectype& bnp = cache().get("BNP", codbnp); // Dettagli fatture pagate const TRecord_array reff(eff.get(EFF_NPROGTR), LF_REFFETTI); for (int r = reff.first_row(); r > 0 && r <= reff.last_row(); r = reff.succ_row(r)) { const TRectype& riga = reff[r]; TSEPA_record rata("SERIF00F"); rata.set("SERF01", PmtInfId); rata.set("SERF02", instr_id); rata.set("SERF03", (long)r); TString doc_id; doc_id << riga.get_date(REFF_DATAFATT).year() << '-' << riga.get(REFF_NFATT); doc_id.strip_spaces(); rata.set("SERF07", doc_id); rata.set("SERF08", riga.get_date(REFF_DATAFATT)); rata.set("SERF09", riga.get_real(REFF_IMPFATT)); rata.set("SERF11", riga.get_real(REFF_IMPORTO)); rata.set("SERF12", cmndt); // Codice mandato rata.set("SERF13", dmndt); // Data mandato rata.insert(); } return imp; } bool TSEPA_app::genera_dist_RID(char td, long nd, TLog_report& log) { TDistinta dist(td, nd); TAssoc_array subdist; for (int i = 0; i < dist.items(); i++) { const TEffetto& eff = dist[i]; const TDate data = data_scadenza(eff); TString8 key; key << data.date2ansi(); TArray* list = (TArray*)subdist.objptr(key); if (list == NULL) { list = new TArray; subdist.add(key, list); } list->add(eff); } if (subdist.empty()) return false; // Identificativo univoco messaggio TString msg_id; SEPA_MsgId(dist[0], msg_id); TString8 msg_date, msg_time; AS400_date_time(msg_date, msg_time); // In AS400 non si gestiscono le sottodistinte, per cui creo una distinta per ogni sottodisitinta FOR_EACH_ASSOC_OBJECT(subdist, hobj, key, itm) { const TArray& trans = objptr2array(itm); TSEPA_record head("SERIT00F"); // Il codice univoco diventa MsgId + Id TString PmtInfId; PmtInfId << msg_id << '-' << key; log.log(0, PmtInfId); head.set("SERT01", PmtInfId); // MsgId // Nel file XML viene richiesta data/ora in formato ISO, qui invece ci sono due campi distinti ą la AS400 head.set("SERT02", msg_date); // CreDtTm data head.set("SERT021", msg_time); // CreDtTm ora head.set("SERT03", long(trans.items())); // NbOfTxs head.set("SERT04", ZERO); // totale head.set("SERT05", _ditta.ragione_sociale()); // Nm const TEffetto& e0 = dist[0]; TString16 codtab = e0.get(EFF_CODABIP); codtab << e0.get(EFF_CODCABP); TString4 prog = e0.get(EFF_PROGBNP); if (prog.full()) codtab << prog; const TRectype& bnp = cache().get("BNP", codtab); TString8 cuc = bnp.get("S5"); if (cuc.blank()) cuc = _cuc; head.set("SERT06", cuc); // Id head.set("SERT07", "CBI"); // Issr head.set("SERT08", PmtInfId); // Progressivo head.set("SERT09", "DD"); head.set("SERT10", "SEPA"); head.set("SERT11", "CORE"); // Codice strumento (CORE, COR1, B2B) const TString8 ts = tipo_sequenza(e0); head.set("SERT12", ts); // Tipo sequenza incasso head.set("SERT13", "SUPP"); const TDate ReqdExctnDt = data_scadenza(e0); head.set("SERT14", ReqdExctnDt); // Data richiesta dal mittente YYYY-MM-DD head.set("SERT15", _ditta.ragione_sociale()); head.set("SERT16", "ADDR"); //head.set("SERT17", "Divisione"); //head.set("SERT18", "Sotto-divisione"); head.set("SERT19", _ditta.via_residenza()); head.set("SERT20", _ditta.civico_residenza()); head.set("SERT21", _ditta.CAP_residenza()); head.set("SERT22", _ditta.comune_residenza()); head.set("SERT23", _ditta.provincia_residenza()); head.set("SERT24", _ditta.stato_partita_IVA()); // Alpha-2 code head.set("SERT25", _ditta.fisica() ? 1L : 0L); head.set("SERT27", cuc); // Id head.set("SERT28", "CBI"); // Issr const TString& iban = bnp.get("S3"); TString msg; if (iban.blank() || iban_check(iban, msg) != 0) { msg.insert(TR("IBAN errato: ")); log.log(2, msg); } head.set("SERT29", iban); // IBAN head.set("SERT30", codtab.left(5)); head.set("SERT31", _ditta.ragione_sociale()); head.set("SERT32", "ADDR"); //head.set("SERT33", "Divisione"); //head.set("SERT34", "Sotto-divisione"); head.set("SERT35", _ditta.via_residenza()); head.set("SERT36", _ditta.civico_residenza()); head.set("SERT37", _ditta.CAP_residenza()); head.set("SERT38", _ditta.comune_residenza()); head.set("SERT39", _ditta.provincia_residenza()); head.set("SERT40", _ditta.stato_partita_IVA()); // Alpha-2 code const TString& cid = bnp.get("S4"); if (cid.blank()) { msg = TR("Creditor ID mancante sulla banca di presentazione"); log.log(2, msg); } head.set("SERT41", cid); head.set("SERT42", "SEPA"); head.set("SERT43", "ADE"); head.set("SERT44", cid.left(2)); // Alpha-2 code real totale; FOR_EACH_ARRAY_ITEM(trans, r, peff) { const TEffetto& e = *(TEffetto*)peff; totale += genera_RID(PmtInfId, e, log); } head.set("SERT04", totale); head.insert(); } completa(dist); return true; } bool TSEPA_app::genera_SEPA_RID(const TMask& msk, TLog_report& log) { xvt_sql_begin(_db); TString query; query = "DELETE FROM SERIT00F;"; xvt_sql_execute(_db, query, NULL, 0L); query = "DELETE FROM SERID00F;"; xvt_sql_execute(_db, query, NULL, 0L); query = "DELETE FROM SERIF00F;"; xvt_sql_execute(_db, query, NULL, 0L); const TSheet_field& sht = msk.sfield(F_SHEET); TRecnotype nd = 0; FOR_EACH_SHEET_ROW(sht, r, row) if (row->get_char(0) == 'X') { char tipo = '\0'; row->get(1, tipo); long numd = 0L; row->get(2, numd); if (genera_dist_RID(tipo, numd, log)) nd++; } if (nd > 0) xvt_sql_commit(_db); else xvt_sql_rollback(_db); return nd > 0; } bool TSEPA_app::convert_apos(const TString& xml) const { TString line(1024); char* buf = line.get_buffer(); TString_array text; FILE* txt = NULL; fopen_s(&txt, xml, "r"); if (!txt) return false; bool converted = false; while (!feof(txt)) { fgets(buf, line.size(), txt); for(char* a = buf; a;) { char* apos = strstr(a, "'"); if (apos) { memcpy(apos, "'", 6); converted = true; a = apos+6; } else break; } text.add(line); } fclose(txt); txt = NULL; if (converted) { fopen_s(&txt, xml, "w"); if (txt) { FOR_EACH_ARRAY_ROW(text, i, row) fputs(*row, txt); fclose(txt); } } return txt != NULL; } bool TSEPA_app::genera_xml(int tipo, bool tuttapost) { #define SEPABASE "SIAGGSE" TFilename tmp = SEPABASE; tmp.add("ConnectionDefault"); if (!tmp.exist()) { char buff[128] = { 0 }; time_t t = { 0 }; localtime(&t); ctime_s(buff, sizeof(buff), &t); ofstream o(tmp); o << "#Connection Default Properties" << endl; o << '#' << buff << endl; } if (tmp.exist()) { TJava_profile prop(tmp); TFilename dbu; dbu << "jdbc:sqlite:" << _dbname; prop.set("database.type", "sqllite"); prop.set("database.url", dbu); prop.set("classForName", "org.sqlite.JDBC"); if (tipo == 8) { prop.set("path.SEPA.RID", _drid); prop.set("path.SEPA.RID.log", _drid); } else { prop.set("path.SEPA.BON", _dbon); prop.set("path.SEPA.BON.log", _dbon); } } if (tipo == 8) tmp = SEPABASE"\\lancioSDDxx.bat"; else tmp = SEPABASE"\\lancioSCTxx.bat"; tmp.make_absolute_path(); const time_t tStart = ::time(NULL); DIRECTORY old_dir; xvt_fsys_get_dir(&old_dir); DIRECTORY new_dir; xvt_fsys_convert_str_to_dir(tmp.path(), &new_dir); xvt_fsys_set_dir(&new_dir); TString8 str_firm; str_firm.format("%05d", prefix().get_codditta()); tmp << ' ' << str_firm; TExternal_app app(tmp); const bool good = app.run() == NOERR; if (good) xvt_sys_sleep(3000); else error_box(FR("Impossibile eseguire %s"), (const char*)tmp); xvt_fsys_set_dir(&old_dir); if (good && tuttapost) { TString_array xmls; TFilename f = tipo == 8 ? _drid : _dbon; f.add("*.xml"); list_files(f, xmls); FOR_EACH_ARRAY_ROW(xmls, r, xml) { const time_t tXml = xvt_fsys_file_attr(*xml, XVT_FILE_ATTR_MTIME); if (tXml >= tStart) convert_apos(*xml); } } return good; } void TSEPA_app::main_loop() { TSEPA_mask m; while (m.run() == K_ENTER) { const int tipo = m.get_int(F_TIPO); const bool tuttapost = m.get_bool(F_APOS); TLog_report log(tipo == 8 ? "RID SEPA" : "Bonifici SEPA"); if (_cuc.blank()) { log.log(2, TR("E' necessario inserire il codice CUC nei parametri degli effetti")); _cuc = "GEB00000"; } if (tipo == 9) genera_SEPA_BON(m, log); else genera_SEPA_RID(m, log); genera_xml(tipo, tuttapost); log.preview(); } } int ef1100(int argc, char* argv[]) { TSEPA_app a; a.run(argc, argv, TR("Bonifici/RID SEPA")); return 0; }