Files correlati : fp0.exe Commento : - Sistemata funzione cco, non andava a causa del programma diverso rispetto a dove era prima - Aggiunta gestione commesse sanità - Tolto else inutile in TFP_expression::parse_search()
645 lines
16 KiB
C++
645 lines
16 KiB
C++
#include "fplib.h"
|
||
#include <fpcust.h>
|
||
#include <fpccaus.h>
|
||
#include <fpcart.h>
|
||
#include <fpcadg.h>
|
||
|
||
/******************************************************************************
|
||
* TFP_custom
|
||
*******************************************************************************/
|
||
|
||
void TFP_custom::init()
|
||
{
|
||
add_file(LF_FPCCAUS, FPCCAUS_NRIGA);
|
||
add_file(LF_FPCART, FPCART_NRIGA);
|
||
add_file(LF_FPCADG, FPCADG_NRIGA);
|
||
}
|
||
|
||
int TFP_custom::write_rewrite(TBaseisamfile& f, bool re) const
|
||
{
|
||
return TMultiple_rectype::write_rewrite(f, re);
|
||
}
|
||
|
||
bool TFP_custom::load(const char* codcust)
|
||
{
|
||
// Metto il codice nel rectype principale ed effettuo una read
|
||
put(FPCUST_CODICE, codcust);
|
||
|
||
return _loaded = TMultiple_rectype::read() == NOERR;
|
||
}
|
||
|
||
void TFP_custom::autoload(const TSheet_field& sf, const int file) const
|
||
{
|
||
switch(file)
|
||
{
|
||
case LF_FPCCAUS:
|
||
{
|
||
TRecord_array& rcaus = body(file);
|
||
rcaus.destroy_rows();
|
||
FOR_EACH_SHEET_ROW(sf, r, row)
|
||
{
|
||
TRectype& rec_row = rcaus.row(-1, true);
|
||
rec_row.put(FPCCAUS_VALORE, row->get(0));
|
||
rec_row.put(FPCCAUS_COND, row->get());
|
||
}
|
||
}
|
||
break;
|
||
case LF_FPCART:
|
||
{
|
||
TRecord_array& rart = body(file);
|
||
rart.destroy_rows();
|
||
FOR_EACH_SHEET_ROW(sf, r, row)
|
||
{
|
||
TRectype& rec_row = rart.row(-1, true);
|
||
rec_row.put(FPCART_TIPO, row->get(0));
|
||
rec_row.put(FPCART_VALORE, row->get());
|
||
rec_row.put(FPCART_COND, row->get());
|
||
}
|
||
}
|
||
break;
|
||
case LF_FPCADG:
|
||
{
|
||
TRecord_array& radg = body(file);
|
||
radg.destroy_rows();
|
||
FOR_EACH_SHEET_ROW(sf, r, row)
|
||
{
|
||
TRectype& rec_row = radg.row(-1, true);
|
||
rec_row.put(FPCADG_TIPODATO, row->get(0));
|
||
rec_row.put(FPCADG_RTESTO, row->get());
|
||
rec_row.put(FPCADG_RNUMERO, row->get());
|
||
rec_row.put(FPCADG_RDATA, row->get());
|
||
rec_row.put(FPCADG_TIPORIGA, row->get());
|
||
rec_row.put(FPCADG_COND, row->get());
|
||
rec_row.put(FPCADG_SPLIT, row->get());
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
fatal_box("File non riconosciuto %d", file);
|
||
break;
|
||
}
|
||
}
|
||
|
||
bool TFP_custom::load_caus_paf(TPaf_record& paf3400f, TDocumento& doc, TDoc_fp& doc_fp) const
|
||
{
|
||
long row_paf = 0L;
|
||
FOR_EACH_FPCAUS_ROW(*this, r, rec_caus)
|
||
{
|
||
if (TFP_expression::check_condition(rec_caus->get(FPCCAUS_COND), doc))
|
||
{
|
||
// Evito il reset visto che setto sempre i campi
|
||
paf3400f.set("PZ_LINEA", ++row_paf);
|
||
paf3400f.set("PZ_CAUSALE", TFP_expression::parse_expression(rec_caus->get(FPCCAUS_VALORE), doc).as_string().left(200)); // 200 caratteri max
|
||
if (!doc_fp.insert(paf3400f))
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool TFP_custom::load_articolo_paf(TPaf_record& paf1900f, TRiga_documento& rdoc, const long row_num, TDoc_fp& doc_fp) const
|
||
{
|
||
long row_paf = 0L;
|
||
FOR_EACH_FPART_ROW(*this, r, rec_art)
|
||
{
|
||
// Controllo se <20> abilitato
|
||
if (TFP_expression::check_condition(rec_art->get(FPCART_COND), rdoc))
|
||
{
|
||
doc_fp.reset(paf1900f);
|
||
paf1900f.set("PY_KEYNLINEA", row_num);
|
||
paf1900f.set("PY_KEYNLINAR", ++row_paf);
|
||
paf1900f.set("PY_TIPOARTICOLO", TFP_expression::parse_expression(rec_art->get(FPCART_TIPO), rdoc).as_string().left(35)); // 35 caratteri max
|
||
paf1900f.set("PY_VALOREARTICOLO", TFP_expression::parse_expression(rec_art->get(FPCART_VALORE), rdoc).as_string().left(35)); // 35 caratteri max
|
||
if (!doc_fp.insert(paf1900f))
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool TFP_custom::load_adg_paf(TPaf_record& paf2100f, TRiga_documento& rdoc, TDoc_fp& doc_fp, const long row_num, const bool split) const
|
||
{
|
||
FOR_EACH_FPADG_ROW(*this, r, rec_adg)
|
||
{
|
||
|
||
if (split != rec_adg->get_bool(FPCADG_SPLIT))
|
||
continue;
|
||
|
||
// Controllo che la riga sia uguale
|
||
if (rec_adg->get(FPCADG_TIPORIGA).full() && rdoc.tipo().codice() != rec_adg->get(FPCADG_TIPORIGA))
|
||
continue;
|
||
|
||
// Controllo la condizione
|
||
if (TFP_expression::check_condition(rec_adg->get(FPCADG_COND), rdoc))
|
||
{
|
||
doc_fp.reset(paf2100f);
|
||
paf2100f.set("PK_KEYNLINEA", static_cast<long>(row_num));
|
||
paf2100f.set("PK_KEYNLINAR", doc_fp._idx_adg_doc_row++);
|
||
paf2100f.set("PK_TIPODATO", TFP_expression::parse_expression(rec_adg->get(FPCADG_TIPODATO), rdoc).as_string().left(10)); // 10 caratteri max
|
||
paf2100f.set("PK_RIFDATO", TFP_expression::parse_expression(rec_adg->get(FPCADG_RTESTO), rdoc).as_string().left(60)); // 60 caratteri max
|
||
paf2100f.set("PK_RIFDATA", TFP_expression::parse_expression(rec_adg->get(FPCADG_RDATA), rdoc));
|
||
paf2100f.set("PK_RIFNUMERO", TFP_expression::parse_expression(rec_adg->get(FPCADG_RNUMERO), rdoc));
|
||
if (!doc_fp.insert(paf2100f))
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool TFP_custom::has_adg_split()
|
||
{
|
||
FOR_EACH_FPADG_ROW(*this, r, rec_adg)
|
||
{
|
||
if (rec_adg->get_bool(FPCADG_SPLIT))
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
TFP_custom::TFP_custom() : TMultiple_rectype(LF_FPCUST), _codcust(""), _loaded(false)
|
||
{
|
||
init();
|
||
}
|
||
|
||
TFP_custom::TFP_custom(const TRectype& rec) : TMultiple_rectype(rec)
|
||
{
|
||
_codcust = get(FPCUST_CODICE);
|
||
_loaded = _codcust.full();
|
||
init();
|
||
}
|
||
|
||
TFP_custom::TFP_custom(const char* codcust) : TFP_custom()
|
||
{
|
||
init();
|
||
load(codcust);
|
||
}
|
||
|
||
/******************************************************************************
|
||
* TFP_custom_cache
|
||
*******************************************************************************/
|
||
|
||
bool TFP_custom_cache::has_custom(const TDocumento& doc)
|
||
{
|
||
const long codcli = doc.clifor().codice();
|
||
bool found = false;
|
||
TRectype& rec_cli = doc.clifor();
|
||
if(_cli_custom.find(codcli) != _cli_custom.end())
|
||
{
|
||
found = true;
|
||
}
|
||
// Prima cerco sul cliente
|
||
else if(rec_cli.get(CLI_DISCUSTFP).full())
|
||
{
|
||
// Cliente disabilitato
|
||
_cli_custom.insert(std::pair<long, TString>(codcli, ""));
|
||
}
|
||
else if(rec_cli.get(CLI_CODFPCUST).full())
|
||
{
|
||
// Cliente con personalizzazione
|
||
_cli_custom.insert(std::pair<long, TString>(codcli, rec_cli.get(CLI_CODFPCUST)));
|
||
found = true;
|
||
}
|
||
// Cerco nel tipo documento
|
||
else if(doc.tipo().cod_cust_fp().full())
|
||
{
|
||
// Tipo doc personalizzato
|
||
_cli_custom.insert(std::pair<long, TString>(codcli, doc.tipo().cod_cust_fp()));
|
||
found = true;
|
||
}
|
||
// Customizzazione globale?
|
||
else if(_global_custom != nullptr)
|
||
{
|
||
// Globale
|
||
_cli_custom.insert(std::pair<long, TString>(codcli, _global_custom->get_codcust()));
|
||
found = true;
|
||
}
|
||
|
||
return found;
|
||
}
|
||
|
||
TFP_custom& TFP_custom_cache::get_custom(const TDocumento& doc)
|
||
{
|
||
if(!has_custom(doc))
|
||
{
|
||
static TFP_custom null_cust;
|
||
return null_cust;
|
||
}
|
||
|
||
const TString& codcust = _cli_custom[doc.clifor().codice()];
|
||
|
||
// Provo a vedere se <20> gi<67> presente nella mappa, altrimento lo aggiungo
|
||
if(_customs.find(codcust) == _customs.end())
|
||
{
|
||
TFP_custom custcli(codcust);
|
||
_customs.insert(std::pair<TString, TFP_custom>(codcust, custcli));
|
||
}
|
||
|
||
return _customs[codcust];
|
||
}
|
||
|
||
TFP_custom_cache::TFP_custom_cache()
|
||
{
|
||
_customs.clear();
|
||
_cli_custom.clear();
|
||
|
||
// Cerco se esiste una custom globale
|
||
TLocalisamfile local_cust(LF_FPCUST);
|
||
|
||
for(int ok = local_cust.first(); ok == NOERR; ok = local_cust.next())
|
||
{
|
||
if(local_cust.get_bool(FPCUST_GLOBAL))
|
||
{
|
||
_global_custom.reset(new TFP_custom(local_cust.curr()));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/******************************************************************************
|
||
* TFP_expression
|
||
*******************************************************************************/
|
||
|
||
/*
|
||
* Sintassi:
|
||
* READ(TABELLA, CAMPO) -> legge dalle tabelle predefinite
|
||
* SEARCH(TABELLA.[CHIAVE], CAMPO, RICERCA) -> legge da una tabella qualsiasi
|
||
* SEARCH e READ sono diverse sotto richiesta di Ilaria per avere una sintassi pi<70> chiara // Ormai hanno sintassi un po' diversa quindi MEGLIO COSI'
|
||
* resto: TExpr_documento()
|
||
*/
|
||
const TVariant TFP_expression::parse_expression(const TString& expr, TRiga_documento& rdoc)
|
||
{
|
||
const TToken_string exprs(expr, '+');
|
||
TVariant result;
|
||
TExpr_documento expr_documento(_mixexpr, &rdoc.doc(), &rdoc);
|
||
|
||
FOR_EACH_STR_TOKEN(exprs, str)
|
||
{
|
||
// Come prima cosa trimmo la vita
|
||
str.trim();
|
||
if (str.starts_with("READ(", true))
|
||
{
|
||
result.add(parse_read(str, rdoc));
|
||
}
|
||
else if (str.starts_with("SEARCH(", true))
|
||
{
|
||
result.add(parse_search(str, rdoc));
|
||
}
|
||
else
|
||
{
|
||
// Controllo non sia una ricerca a DB con il punto
|
||
if (expr.contains("->"))
|
||
{
|
||
// Se contiene una freccia non posso fare un TToken_string easy
|
||
// Non esistono campi con -> vero?
|
||
str.replace("->", ".");
|
||
}
|
||
|
||
// Questo diventa true se sono passato sopra o effettivamente ha un punto dall'inizio
|
||
if (expr.contains('.'))
|
||
{
|
||
TToken_string simple_field(expr, '.');
|
||
result.add(do_read(simple_field.get(0), simple_field.get(), rdoc));
|
||
}
|
||
// Se inizia come una stringa o non contiene parentesi -> no expr
|
||
else if (str.starts_with("\"") || !(str.contains("(") && str.contains(")")))
|
||
{
|
||
str.replace("\"", "");
|
||
// Cerchiamo di capire che <20>
|
||
result.add(parse_var(str));
|
||
}
|
||
else
|
||
{
|
||
expr_documento.set(str, _mixexpr);
|
||
result.add(expr_documento.as_string());
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
bool TFP_expression::check_condition(const TString& cond, TRiga_documento& rdoc)
|
||
{
|
||
// Caso base
|
||
if (cond.empty())
|
||
return true;
|
||
|
||
TString cond_sx, cond_dx;
|
||
TFP_operator symb;
|
||
split_condition(cond, cond_sx, cond_dx, symb);
|
||
|
||
// Se ho davanti un and o un or evito di fare i passaggi dopo perch<63> sono inutili
|
||
// Cos<6F> facendo posso anche concatenare pi<70> and o or, sarebbe carino aggiungere anche il supporto alle parentesi
|
||
if(symb == and)
|
||
{
|
||
return check_condition(cond_sx, rdoc) && check_condition(cond_dx, rdoc);
|
||
}
|
||
if(symb == or)
|
||
{
|
||
return check_condition(cond_sx, rdoc) || check_condition(cond_dx, rdoc);
|
||
}
|
||
|
||
// Altrimenti proseguo per la mia solita strada
|
||
const TVariant value_sx = parse_expression(cond_sx, rdoc);
|
||
const TVariant value_dx = parse_expression(cond_dx, rdoc);
|
||
|
||
switch(symb)
|
||
{
|
||
case eq:
|
||
return value_sx == value_dx;
|
||
case neq:
|
||
return value_sx != value_dx;
|
||
case gt:
|
||
return value_sx > value_dx;
|
||
case ls:
|
||
return value_sx < value_dx;
|
||
case gteq:
|
||
return value_sx >= value_dx;
|
||
case lseq:
|
||
return value_sx <= value_dx;
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
void TFP_expression::extract_info(const TString& expr, TString& tabella, TString& campo)
|
||
{
|
||
// Prendo la stringa pulita della parte sinistra
|
||
TString clean_expr = expr.mid(expr.find('(') + 1);
|
||
// Tolgo eventuali spazi ai lati
|
||
clean_expr.trim();
|
||
// Tolgo la virgola finale
|
||
clean_expr.rtrim(1);
|
||
TToken_string info(clean_expr, ',');
|
||
|
||
// Trimmare sempre come se non sapessi fare altro nella vita
|
||
tabella.cut(0) << info.remove(0); tabella.trim();
|
||
campo.cut(0) << info.remove(0); campo.trim();
|
||
}
|
||
|
||
|
||
void TFP_expression::extract_info(const TString& expr, TString& tabella, TString& campo, TToken_string& search, int& key)
|
||
{
|
||
// Prendo la stringa pulita della parte sinistra
|
||
TString clean_expr = expr.mid(expr.find('(') + 1);
|
||
// Tolgo eventuali spazi ai lati
|
||
clean_expr.trim();
|
||
// Tolgo la virgola finale
|
||
clean_expr.rtrim(1);
|
||
TToken_string info(clean_expr, ',');
|
||
|
||
// Trimmare sempre come se non sapessi fare altro nella vita
|
||
tabella.cut(0) << info.remove(0); tabella.trim();
|
||
if(tabella.contains('.'))
|
||
{
|
||
TToken_string app(tabella, '.');
|
||
tabella = app.get();
|
||
key = atoi(app.get());
|
||
}
|
||
else
|
||
{
|
||
key = 1;
|
||
}
|
||
|
||
campo.cut(0) << info.remove(0); campo.trim();
|
||
|
||
// Prendo il resto e lo devolvo nella ricerca
|
||
search.cut(0) << info;
|
||
search.trim();
|
||
search.restart();
|
||
}
|
||
|
||
bool TFP_expression::calc_table(const TString& tabella, int& file)
|
||
{
|
||
file = atoi(tabella);
|
||
if (file == 0)
|
||
file = table2logic(tabella);
|
||
return is_multi_table(file);
|
||
}
|
||
|
||
void TFP_expression::split_condition(const TString& cond, TString& cond_sx, TString& cond_dx, TFP_operator& symb)
|
||
{
|
||
// Cerchiamo di capire che condizione ho
|
||
TString4 symbol;
|
||
symb = error;
|
||
if(cond.contains(" == "))
|
||
{
|
||
symb = eq;
|
||
symbol = " == ";
|
||
}
|
||
else if(cond.contains(" != "))
|
||
{
|
||
symb = neq;
|
||
symbol = " != ";
|
||
}
|
||
else if (cond.contains(" >= "))
|
||
{
|
||
symb = gteq;
|
||
symbol = " >= ";
|
||
}
|
||
else if (cond.contains(" <= "))
|
||
{
|
||
symb = lseq;
|
||
symbol = " <= ";
|
||
}
|
||
else if (cond.contains(" > "))
|
||
{
|
||
symb = gt;
|
||
symbol = " > ";
|
||
}
|
||
else if (cond.contains(" < "))
|
||
{
|
||
symb = ls;
|
||
symbol = " < ";
|
||
}
|
||
else if (cond.contains(" && "))
|
||
{
|
||
symb = and;
|
||
symbol = " && ";
|
||
}
|
||
else if (cond.contains(" || "))
|
||
{
|
||
symb = or;
|
||
symbol = " || ";
|
||
}
|
||
|
||
if(symb != error)
|
||
{
|
||
cond_sx = cond.left(cond.find(symbol));
|
||
cond_dx = cond.right(cond.len() - (cond.find(symbol) + symbol.len()));
|
||
}
|
||
else
|
||
{
|
||
// Considero come se <20> true o no il campo in generale
|
||
symb = gt;
|
||
cond_sx = cond;
|
||
cond_dx.cut(0);
|
||
}
|
||
}
|
||
|
||
// Questa funzione potrebbe diventare standard per TRectype
|
||
TVariant& TFP_expression::get_value(const TRectype& rec, const TString& campo)
|
||
{
|
||
TVariant& ret = get_tmp_var();
|
||
switch(rec.type(campo))
|
||
{
|
||
case _intfld:
|
||
ret.set(rec.get_int(campo));
|
||
break;
|
||
case _longfld:
|
||
ret.set(rec.get_long(campo));
|
||
break;
|
||
case _realfld:
|
||
ret.set(rec.get_real(campo));
|
||
break;
|
||
case _datefld:
|
||
ret.set(rec.get_date(campo));
|
||
break;
|
||
case _wordfld:
|
||
ret.set(rec.get_word(campo));
|
||
break;
|
||
case _charfld:
|
||
ret.set(rec.get_char(campo));
|
||
break;
|
||
case _boolfld:
|
||
ret.set(rec.get_bool(campo));
|
||
break;
|
||
case _nullfld:
|
||
case _alfafld:
|
||
case _intzerofld:
|
||
case _longzerofld:
|
||
case _memofld:
|
||
ret.set(rec.get(campo));
|
||
break;
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
TVariant& TFP_expression::parse_var(const TString& str)
|
||
{
|
||
TVariant& ret = get_tmp_var();
|
||
if (str.empty())
|
||
{
|
||
ret.add(EMPTY_STRING);
|
||
return ret;
|
||
}
|
||
|
||
// Come prima cosa leggo la stringa e cerco di capire se ci sono numeri o no
|
||
bool is_real = false, is_string = false;
|
||
for(int i = 0; i < str.len() && !is_string; i++)
|
||
{
|
||
const char val = str[i];
|
||
if(val < '0' || val > '9')
|
||
{
|
||
if (val == '.' || val == ',')
|
||
is_real = true;
|
||
else
|
||
is_string = true;
|
||
}
|
||
}
|
||
|
||
// Una volta che ho capito che ho davanti tento il trick!
|
||
if (is_string)
|
||
{
|
||
// Tento la data
|
||
const TDate pop(str);
|
||
|
||
if (pop.ok())
|
||
ret.add(pop);
|
||
else
|
||
ret.add(str);
|
||
}
|
||
else if (is_real)
|
||
ret.add(real(str));
|
||
else
|
||
{
|
||
char* err;
|
||
const long long_val = strtol(str, &err, 10);
|
||
// Ho avuto un errore? Il controllo ha fallito!
|
||
if(err && *err)
|
||
{
|
||
ret.add(str);
|
||
}
|
||
else
|
||
{
|
||
ret.add(long_val);
|
||
}
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
TVariant& TFP_expression::parse_read(const TString& str, TRiga_documento& rdoc)
|
||
{
|
||
TString tabella, campo;
|
||
extract_info(str, tabella, campo);
|
||
return do_read(tabella, campo, rdoc);
|
||
}
|
||
|
||
TVariant& TFP_expression::do_read(const TString& tabella, const TString& campo, TRiga_documento& rdoc)
|
||
{
|
||
const TDocumento& doc = rdoc.doc();
|
||
// Prima di tutto controllo se <20> una delle tabelle multitracciato che supporto
|
||
if(tabella == "%TIP")
|
||
{
|
||
return get_value(doc.tipo(), campo);
|
||
}
|
||
|
||
if(tabella == "%NUM")
|
||
{
|
||
return get_value(doc.codice_numerazione(), campo);
|
||
}
|
||
|
||
if(tabella == "%CPG")
|
||
{
|
||
return get_value(cache().get("%CPG", doc.get(DOC_CODPAG)), campo);
|
||
}
|
||
|
||
int file;
|
||
calc_table(tabella, file);
|
||
|
||
switch (file)
|
||
{
|
||
case LF_DOC:
|
||
return get_value(doc, campo);
|
||
case LF_RIGHEDOC:
|
||
return get_value(rdoc, campo);
|
||
case LF_CLIFO:
|
||
return get_value(doc.clifor(), campo);
|
||
case LF_CFVEN:
|
||
return get_value(doc.clifor().vendite(), campo);
|
||
case LF_INDSP:
|
||
return get_value(cache().get(LF_INDSP, doc.get(DOC_CODINDSP)), campo);
|
||
case LF_ANAMAG:
|
||
return get_value(rdoc.articolo(), campo);
|
||
case LF_CODCORR:
|
||
return get_value(cache().get(LF_CODCORR, rdoc.articolo().codice()), campo);
|
||
default:
|
||
static TVariant null_var(EMPTY_STRING);
|
||
return null_var;
|
||
}
|
||
}
|
||
|
||
TVariant& TFP_expression::parse_search(const TString& str, TRiga_documento& rdoc)
|
||
{
|
||
TString tabella, campo;
|
||
TToken_string input_search("", ';'), search;
|
||
int file, key;
|
||
|
||
extract_info(str, tabella, campo, input_search, key);
|
||
const bool multi_table = calc_table(tabella, file);
|
||
|
||
// Parso ogni singolo token della ricerca
|
||
FOR_EACH_TOKEN(input_search, tok)
|
||
{
|
||
search.add(parse_expression(tok, rdoc).as_string());
|
||
}
|
||
|
||
if (multi_table)
|
||
{
|
||
return get_value(cache().get(tabella, search, key), campo);
|
||
}
|
||
|
||
return get_value(cache().get(file, search, key), campo);
|
||
}
|