campo-sirio/src/fp/fplib05.cpp
Mattia Tollari 64a9566522 Patch level : 12.0 866
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()
2019-08-02 11:12:01 +02:00

645 lines
16 KiB
C++
Raw Blame History

#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);
}