#include "fplib.h" #include #include #include #include /****************************************************************************** * 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 è 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(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(codcli, "")); } else if(rec_cli.get(CLI_CODFPCUST).full()) { // Cliente con personalizzazione _cli_custom.insert(std::pair(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(codcli, doc.tipo().cod_cust_fp())); found = true; } // Customizzazione globale? else if(_global_custom != nullptr) { // Globale _cli_custom.insert(std::pair(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 è già presente nella mappa, altrimento lo aggiungo if(_customs.find(codcust) == _customs.end()) { TFP_custom custcli(codcust); _customs.insert(std::pair(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ù 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 è 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è sono inutili // Così facendo posso anche concatenare più 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 è 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 è 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); }