campo-sirio/ef/ef1100.cpp

1441 lines
39 KiB
C++

#include <applicat.h>
#include <automask.h>
#include <dongle.h>
#include <execp.h>
#include <progind.h>
#include <recarray.h>
#include <recset.h>
#include <relation.h>
#include <reputils.h>
#include <tabutil.h>
#include <utility.h>
#include <validate.h>
#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)
head.set("SERT12", "FRST"); // 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, "&#039;");
if (apos)
{
memcpy(apos, "&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;
}