Patch level : 12.0 no-patch
Files correlati : fp Commento : Continuo sviluppo personalizzazioni FP - Creata classe ad-hoc (TFP_expression) per il controllo delle espressioni così da non avere la classe TDoc_fp piena di roba - Aggiunte funzioni comuni e lettura delle condizioni - Inserita gestione delle variabili tramite TVariant (con allocazione furba) per effettuare delle comparazioni migliori
This commit is contained in:
parent
751102232e
commit
2582b6c7b4
@ -175,7 +175,7 @@ BEGIN
|
||||
DISPLAY "Tipo riga@8" CODTAB
|
||||
DISPLAY "Descrizione@50" S0
|
||||
OUTPUT S_ADG_TIPORIGA CODTAB
|
||||
HELP "Condizione che se è vera abilita la riga"
|
||||
HELP "La personalizzazione viene abilitata solo su queste righe"
|
||||
END
|
||||
|
||||
STRING S_ADG_COND 200 50
|
||||
|
@ -269,8 +269,6 @@ protected:
|
||||
bool export_paf0100f();
|
||||
bool export_paf3200f();
|
||||
|
||||
|
||||
|
||||
// Record clifo
|
||||
//void set_rec_clifo(char tipocf, long codcf);
|
||||
|
||||
@ -287,14 +285,6 @@ public:
|
||||
int force_commit();
|
||||
void set_cache_insert(const bool v) { _cache_insert = v; }
|
||||
|
||||
// Test
|
||||
static const TString& parse_expression(const TString& expr, TRiga_documento& rdoc);
|
||||
static TString& parse_read(const TString& str, TRiga_documento& rdoc);
|
||||
static TString& do_read(const TString& tabella, const TString& campo, TRiga_documento& rdoc);
|
||||
static TString& parse_search(const TString& str, TRiga_documento& rdoc);
|
||||
|
||||
|
||||
|
||||
TDoc_fp();
|
||||
~TDoc_fp();
|
||||
};
|
||||
@ -490,4 +480,29 @@ public:
|
||||
for (int __r = bodyof##__radg.first_row(); bodyof##__radg.exist(__r) && (__radg=&(TRectype&)bodyof##__radg.row(__r))!=NULL; __r=bodyof##__radg.succ_row(__r))
|
||||
|
||||
|
||||
class TFP_expression : public TObject
|
||||
{
|
||||
private:
|
||||
enum TFP_operator { error, eq, neq, gt, ls, gteq, lseq };
|
||||
|
||||
// Etrattori
|
||||
static void extract_info(const TString& expr, TString& tabella, TString& campo, TToken_string* search);
|
||||
static bool calc_table(const TString& tabella, int& file);
|
||||
static void split_condition(const TString& cond, TString& cond_sx, TString& cond_dx, TFP_operator& symb);
|
||||
static TVariant& get_value(const TRectype& rec, const TString& campo);
|
||||
static TVariant& parse_var(const TString& str);
|
||||
|
||||
// Calcolatori
|
||||
static TVariant& parse_read(const TString& str, TRiga_documento& rdoc);
|
||||
static TVariant& do_read(const TString& tabella, const TString& campo, TRiga_documento& rdoc);
|
||||
static TVariant& parse_search(const TString& str, TRiga_documento& rdoc);
|
||||
|
||||
public:
|
||||
static const TVariant parse_expression(const TString& expr, TRiga_documento& rdoc);
|
||||
static bool check_condition(const TString& cond, TRiga_documento& rdoc);
|
||||
|
||||
TFP_expression() = default;
|
||||
virtual ~TFP_expression() = default;
|
||||
};
|
||||
|
||||
#endif // __FPLIB_H
|
||||
|
@ -1408,164 +1408,6 @@ bool TDoc_fp::export_paf3200f()
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sintassi:
|
||||
* READ(TABELLA, CAMPO) -> legge dalle tabelle predefinite
|
||||
* SEARCH(TABELLA, CAMPO, RICERCA) -> legge da una tabella qualsiasi
|
||||
* SEARCH e READ sono diverse sotto richiesta di Ilaria per avere una sintassi più chiara
|
||||
* resto: TExpr_documento()
|
||||
*/
|
||||
const TString& TDoc_fp::parse_expression(const TString& expr, TRiga_documento& rdoc)
|
||||
{
|
||||
const TToken_string exprs(expr, '+');
|
||||
TString& result = get_tmp_string().cut(0);
|
||||
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 << parse_read(str, rdoc);
|
||||
} else if(str.starts_with("SEARCH(", true))
|
||||
{
|
||||
result << 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 << 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("\"", "");
|
||||
result << str;
|
||||
}
|
||||
else
|
||||
{
|
||||
expr_documento.set(str, _mixexpr);
|
||||
result << expr_documento.as_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void extract_info(const TString& expr, TString& tabella, TString& campo, TToken_string* search)
|
||||
{
|
||||
// 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();
|
||||
if(search != nullptr)
|
||||
{
|
||||
// Prendo il resto
|
||||
search->cut(0) << info;
|
||||
search->trim();
|
||||
search->restart();
|
||||
}
|
||||
}
|
||||
|
||||
bool calc_table(const TString& tabella, int& file)
|
||||
{
|
||||
file = atoi(tabella);
|
||||
if (file == 0)
|
||||
file = table2logic(tabella);
|
||||
return is_multi_table(file);
|
||||
}
|
||||
|
||||
TString& TDoc_fp::parse_read(const TString& str, TRiga_documento& rdoc)
|
||||
{
|
||||
TString tabella, campo;
|
||||
|
||||
extract_info(str, tabella, campo, nullptr);
|
||||
|
||||
return do_read(tabella, campo, rdoc);
|
||||
}
|
||||
|
||||
TString& TDoc_fp::do_read(const TString& tabella, const TString& campo, TRiga_documento& rdoc)
|
||||
{
|
||||
TString& result = get_tmp_string().cut(0);
|
||||
int file;
|
||||
|
||||
bool multi_table = calc_table(tabella, file);
|
||||
|
||||
switch (file)
|
||||
{
|
||||
case LF_DOC:
|
||||
result = rdoc.doc().get(campo);
|
||||
break;
|
||||
case LF_RIGHEDOC:
|
||||
result = rdoc.get(campo);
|
||||
break;
|
||||
case LF_CLIFO:
|
||||
result = rdoc.doc().clifor().get(campo);
|
||||
break;
|
||||
case LF_CFVEN:
|
||||
result = rdoc.doc().clifor().vendite().get(campo);
|
||||
break;
|
||||
case LF_LETINT:
|
||||
result = rdoc.doc().clifor().lettera().get(campo);
|
||||
break;
|
||||
case LF_ANAMAG:
|
||||
result = rdoc.articolo().get(campo);
|
||||
break;
|
||||
default:
|
||||
// Skip
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TString& TDoc_fp::parse_search(const TString& str, TRiga_documento& rdoc)
|
||||
{
|
||||
TString& result = get_tmp_string();
|
||||
|
||||
TString tabella, campo;
|
||||
TToken_string input_search, search;
|
||||
int file;
|
||||
|
||||
extract_info(str, tabella, campo, &input_search);
|
||||
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));
|
||||
}
|
||||
|
||||
if(multi_table)
|
||||
{
|
||||
result.cut(0) << cache().get(tabella, search, campo);
|
||||
} else
|
||||
{
|
||||
result.cut(0) << cache().get(file, search, campo);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool TDoc_fp::doc_to_paf(TDocumentoEsteso& doc)
|
||||
{
|
||||
if (!initialize(doc))
|
||||
|
@ -37,6 +37,7 @@ void TFP_custom::autoload(const TSheet_field& sf, const int file) const
|
||||
{
|
||||
TRectype& rec_row = rcaus.row(-1, true);
|
||||
rec_row.put(FPCCAUS_VALORE, row->get(0));
|
||||
rec_row.put(FPCCAUS_COND, row->get());
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -49,6 +50,7 @@ void TFP_custom::autoload(const TSheet_field& sf, const int file) const
|
||||
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;
|
||||
@ -63,6 +65,9 @@ void TFP_custom::autoload(const TSheet_field& sf, const int file) const
|
||||
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_SPLIT, row->get());
|
||||
rec_row.put(FPCADG_COND, row->get());
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -89,3 +94,315 @@ TFP_custom::TFP_custom(const char* codcust) : TFP_custom()
|
||||
init();
|
||||
load(codcust);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Sintassi:
|
||||
* READ(TABELLA, CAMPO) -> legge dalle tabelle predefinite
|
||||
* SEARCH(TABELLA, CAMPO, RICERCA) -> legge da una tabella qualsiasi
|
||||
* SEARCH e READ sono diverse sotto richiesta di Ilaria per avere una sintassi più chiara
|
||||
* 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 è
|
||||
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)
|
||||
{
|
||||
TString cond_sx, cond_dx;
|
||||
TFP_operator symb;
|
||||
split_condition(cond, cond_sx, cond_dx, symb);
|
||||
|
||||
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, TToken_string* search)
|
||||
{
|
||||
// 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();
|
||||
if (search != nullptr)
|
||||
{
|
||||
// Prendo il resto
|
||||
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;
|
||||
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 = " < ";
|
||||
}
|
||||
|
||||
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 è 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();
|
||||
|
||||
// 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, nullptr);
|
||||
return do_read(tabella, campo, rdoc);
|
||||
}
|
||||
|
||||
TVariant& TFP_expression::do_read(const TString& tabella, const TString& campo, TRiga_documento& rdoc)
|
||||
{
|
||||
int file;
|
||||
calc_table(tabella, file);
|
||||
|
||||
switch (file)
|
||||
{
|
||||
case LF_DOC:
|
||||
return get_value(rdoc.doc(), campo);
|
||||
case LF_RIGHEDOC:
|
||||
return get_value(rdoc, campo);
|
||||
case LF_CLIFO:
|
||||
return get_value(rdoc.doc().clifor(), campo);
|
||||
case LF_CFVEN:
|
||||
return get_value(rdoc.doc().clifor().vendite(), campo);
|
||||
case LF_LETINT:
|
||||
return get_value(rdoc.doc().clifor().lettera(), campo);
|
||||
case LF_ANAMAG:
|
||||
return get_value(rdoc.articolo(), 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;
|
||||
|
||||
extract_info(str, tabella, campo, &input_search);
|
||||
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), campo);
|
||||
}
|
||||
else
|
||||
{
|
||||
return get_value(cache().get(file, search), campo);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user