From d6bef9b4e2075c69b5d8eaf4ca99afc0dd0be8db Mon Sep 17 00:00:00 2001 From: Simone Palacino Date: Thu, 28 May 2020 16:33:29 +0200 Subject: [PATCH 1/2] Patch level : 12.0 964 Files correlati : ba0.exe f90.exe f90100d.msk f90200a.msk f90300a.msk f90300b.msk f181.dir f181.trr f181.des f183.dir f183.trr f183.des Commento : - Aumentato campo FILENAME nelle tabelle. - Aggiunto alle cat. annessi se obbligatorio. - Aggiunto controllo annessi obbligatori in diagnostica. - Corretta esportazione IVA per categorie annessi: ora prende il nome della cat. - Implementati nuovi metodi per le categorie doc. - Modificati sheet categorie e annessi - Dopo la conferma del nuovo annesso la maschera non usciva. - Ora gli annessi sono univoci per ogni categoria "padre" - Aggiunto controllo univocita' annesso in inserimento - Modificata finestra nuovo annesso utilizzata anche per modificare una cat. annesso esistente - Semplificato notevolmente riempimento sheet annessi - Tolta colonna inutilizzata nelle categorie documentali - Aggiunta possibilita' di modificare i file e annessi (solo alcuni campi) - Durante l'inserimento di un file annesso non viene piu' richiesta la cat. padre poiche' non avrebbe senso, visto che e' il programma che deve riconoscere a quale cat. appartiene, quindi viene presa in automatico, e caricati autom. le cat annessi. - Aggiunto controllo esistenza file annessi come per i doc. - Aggiunti test sulle classi per le categorie documentali. --- build/f90.vcxproj | 4 - build/f90.vcxproj.filters | 8 - exe/recdesc/f181.dir | 2 +- exe/recdesc/f181.trr | 2 +- exe/recdesc/f183.dir | 2 +- exe/recdesc/f183.trr | 2 +- src/f9/f181.dir | 2 +- src/f9/f181.trr | 2 +- src/f9/f183.dir | 2 +- src/f9/f183.trr | 2 +- src/f9/f90200.cpp | 196 ++++++++++++-------- src/f9/f90200a.h | 3 +- src/f9/f90200a.uml | 13 +- src/f9/f90300.cpp | 185 +++++++++++++++++-- src/f9/f90300a.uml | 36 ++++ src/f9/f90300b.uml | 2 +- src/f9/f90400.cpp | 143 ++++++++++++--- src/f9/f9lib01.cpp | 377 ++++++++++++++++++++++++++------------ src/f9/f9lib01.h | 73 +++++--- src/f9/f9lib02.cpp | 1 - 20 files changed, 750 insertions(+), 307 deletions(-) diff --git a/build/f90.vcxproj b/build/f90.vcxproj index e6990731a..1cff739c7 100644 --- a/build/f90.vcxproj +++ b/build/f90.vcxproj @@ -233,10 +233,6 @@ true - - - - diff --git a/build/f90.vcxproj.filters b/build/f90.vcxproj.filters index 37b2e9e67..6adb7987a 100644 --- a/build/f90.vcxproj.filters +++ b/build/f90.vcxproj.filters @@ -118,14 +118,6 @@ SQL - - - Recdesc - - - Recdesc - - Recdesc diff --git a/exe/recdesc/f181.dir b/exe/recdesc/f181.dir index 9af2df0c8..eb9b5fda4 100644 --- a/exe/recdesc/f181.dir +++ b/exe/recdesc/f181.dir @@ -1,3 +1,3 @@ 181 0 -$docf9|3|3|96|0|Associazione doc cartacei / mov per f9||| +$docf9|3|6|132|0|Associazione doc cartacei / mov per f9||| diff --git a/exe/recdesc/f181.trr b/exe/recdesc/f181.trr index d33d129c0..f091adde1 100644 --- a/exe/recdesc/f181.trr +++ b/exe/recdesc/f181.trr @@ -1,7 +1,7 @@ 181 4 NUMREG|3|7|0|Numero di registrazione associato -FILENAME|1|64|0|Nome del file (senza indirizzo) +FILENAME|1|100|0|Nome del file (senza indirizzo) LOADDATE|5|8|0|Data caricamento USER|1|16|0|Utente che ha caricato il file 3 diff --git a/exe/recdesc/f183.dir b/exe/recdesc/f183.dir index d07401198..dcf573601 100644 --- a/exe/recdesc/f183.dir +++ b/exe/recdesc/f183.dir @@ -1,3 +1,3 @@ 183 0 -$annessif9|9|9|116|0|Tabella file annessi F9||| +$annessif9|1|1|152|0|Tabella file annessi F9||| diff --git a/exe/recdesc/f183.trr b/exe/recdesc/f183.trr index f89496499..b860e515a 100644 --- a/exe/recdesc/f183.trr +++ b/exe/recdesc/f183.trr @@ -1,7 +1,7 @@ 183 6 NUMREG|3|7|0|Numero di registrazione associato -FILENAME|1|64|0|Nome del file (senza indirizzo) +FILENAME|1|100|0|Nome del file (senza indirizzo) CATDOCPAD|1|10|0|Categoria documentale padre (appartenenza annesso) CATDOCANN|1|10|0|Categoria documentale annesso LOADDATE|5|8|0|Data caricamento diff --git a/src/f9/f181.dir b/src/f9/f181.dir index 9af2df0c8..eb9b5fda4 100644 --- a/src/f9/f181.dir +++ b/src/f9/f181.dir @@ -1,3 +1,3 @@ 181 0 -$docf9|3|3|96|0|Associazione doc cartacei / mov per f9||| +$docf9|3|6|132|0|Associazione doc cartacei / mov per f9||| diff --git a/src/f9/f181.trr b/src/f9/f181.trr index d33d129c0..f091adde1 100644 --- a/src/f9/f181.trr +++ b/src/f9/f181.trr @@ -1,7 +1,7 @@ 181 4 NUMREG|3|7|0|Numero di registrazione associato -FILENAME|1|64|0|Nome del file (senza indirizzo) +FILENAME|1|100|0|Nome del file (senza indirizzo) LOADDATE|5|8|0|Data caricamento USER|1|16|0|Utente che ha caricato il file 3 diff --git a/src/f9/f183.dir b/src/f9/f183.dir index d07401198..dcf573601 100644 --- a/src/f9/f183.dir +++ b/src/f9/f183.dir @@ -1,3 +1,3 @@ 183 0 -$annessif9|9|9|116|0|Tabella file annessi F9||| +$annessif9|1|1|152|0|Tabella file annessi F9||| diff --git a/src/f9/f183.trr b/src/f9/f183.trr index f89496499..b860e515a 100644 --- a/src/f9/f183.trr +++ b/src/f9/f183.trr @@ -1,7 +1,7 @@ 183 6 NUMREG|3|7|0|Numero di registrazione associato -FILENAME|1|64|0|Nome del file (senza indirizzo) +FILENAME|1|100|0|Nome del file (senza indirizzo) CATDOCPAD|1|10|0|Categoria documentale padre (appartenenza annesso) CATDOCANN|1|10|0|Categoria documentale annesso LOADDATE|5|8|0|Data caricamento diff --git a/src/f9/f90200.cpp b/src/f9/f90200.cpp index 6fee26e08..8288409f2 100644 --- a/src/f9/f90200.cpp +++ b/src/f9/f90200.cpp @@ -6,6 +6,7 @@ #include "../fp/fplib.h" #include "f901tab.h" #include "f90100.h" +#include "urldefid.h" TString escape(const char* str); @@ -15,7 +16,34 @@ TString escape(const char* str); class TF9_categorie_doc_msk : public TAutomask { - std::unique_ptr _annessi_sheet; + class TNew_annesso_msk : public TMask + { + public: + void edit_mode(const bool edit = true) + { + enable(101, !edit); + } + + void fill_field(const TString& nome_ann, const TString& descr, const TString& tipo_ann, const bool obblig) + { + set(101, nome_ann); + set(102, descr); + set(103, tipo_ann); + set(104, obblig); + } + + TNew_annesso_msk(const char* title) : TMask(title, 1, 78, 13) + { + add_button_tool(DLG_OK, "~Conferma", TOOL_OK); + add_button_tool(DLG_NULL, "", 0); + add_button_tool(DLG_CANCEL, "~Annulla", TOOL_CANCEL); + add_string (101, 0, "Nome tipo annesso:", 1, 1, 10); + add_string (102, 0, "Descrizione: ", 1, 2, 30); + add_list (103, 0, "Tipologia annesso:", 1, 3, 21, "", "DC|RC", "Annesso Cartaceo|Prospetto Rev. Charge"); + add_boolean (104, 0, "Obbligatorio: ", 1, 4); + } + }; + std::shared_ptr _annessi_sheet; TRecord_categorie _categorie_doc; bool on_field_event(TOperable_field& o, TField_event e, long jolly) override; @@ -23,11 +51,12 @@ class TF9_categorie_doc_msk : public TAutomask void check_spell() const; void fill_annessi(const TString& catdoc); - void fill_annessi(const TString_array& annessi); void load_table() const; void salva_tabella() const; public: + static void correct_spell_catdoc(TString& catdoc); + TF9_categorie_doc_msk() : TAutomask("f90200a") { load_table(); } }; @@ -55,44 +84,80 @@ bool TF9_categorie_doc_msk::on_field_event(TOperable_field& o, TField_event e, l sf.show(); } break; - case DLG_USER: - if (e == fe_button) + case DLG_USER: + if (e == fe_button) + { + TSheet_field& sf = sfield(S_CLASSDOC); + const TString catdoc_padre(sf.row(sf.selected()).get(1)); + fill_annessi(catdoc_padre); + int key; + while ((key = _annessi_sheet->run()) != K_ESC) { - TSheet_field& sf = sfield(S_CLASSDOC); - const TString catdoc_padre(sf.row(sf.selected()).get(1)); - const TString tipoann = sf.row(sf.selected()).get(cid2index(F_OPERCEE)); - if (!tipoann.empty()) + if (key == K_INS) // Voglio inserire un nuovo annesso { - warning_box("Impossibile visualizzare gli annessi di una categoria annesso."); - break; - } - fill_annessi(catdoc_padre); - int key; - while ((key = _annessi_sheet->run()) != K_ESC) - { - if (key == K_INS) + _categorie_doc.reload(); + TString title; title << "Nuovo annesso per " << catdoc_padre; + TNew_annesso_msk new_ann(title); + while(new_ann.run() == K_ENTER) { - TArray_sheet* catd = _categorie_doc.get_sheet_catdocs().get(); - if (catd->run() == K_ENTER) + TString catann = new_ann.get(101); + if (catann.empty()) { - _categorie_doc.add_annesso(catdoc_padre, catd->row(catd->selected()).get(0)); + warning_box("Inserire il nome tipo annesso"); + continue; + } + TCategorie_doc::annesso ann; + if (!_categorie_doc.get_ann(catann, ann)) + { + const TString& descr = new_ann.get(102); + const TString& tipo = new_ann.get(103); + const bool obblig = new_ann.get_bool(104); + correct_spell_catdoc(catann); + _categorie_doc.add_annesso(catdoc_padre, catann, descr, tipo, obblig); fill_annessi(catdoc_padre); } + else + warning_box("E' gia' presente un annesso con lo stesso nome per la categoria %s", (const char*)catdoc_padre); + break; } - else if (key == K_DEL) + } + else if (key == K_DEL) + { + const int r = _annessi_sheet->selected(); + if (yesno_box("Eliminare l'annesso N. %d?", r + 1)) { - const int r = _annessi_sheet->selected(); - if (yesno_box("Eliminare l'annesso N. %d?", r + 1)) - { - _categorie_doc.del_annesso(catdoc_padre, _annessi_sheet->row(r).get(0)); - fill_annessi(catdoc_padre); - } + _categorie_doc.del_annesso(catdoc_padre, _annessi_sheet->row(r).get(0)); + fill_annessi(catdoc_padre); + } + } + else if(key == K_CTRL+'F' || key == K_ENTER) // EDIT + { + //_categorie_doc.reload(); + TString title; title << "Modifica annesso per " << catdoc_padre; + TNew_annesso_msk new_ann(title); + new_ann.edit_mode(); + TToken_string& r = _annessi_sheet->row(_annessi_sheet->selected()); + new_ann.fill_field(r.get(0), r.get(1), r.get(2), r.get_bool(3)); + if (new_ann.run() == K_ENTER) + { + const TString catann = new_ann.get(101); + //TCategorie_doc::annesso ann; + /*if (!_categorie_doc.get_ann(catann, ann)) + {*/ + const TString& descr = new_ann.get(102); + const TString& tipo = new_ann.get(103); + const bool obblig = new_ann.get_bool(104); + //correct_spell_catdoc(catann); + _categorie_doc.del_annesso(catdoc_padre, catann); + _categorie_doc.add_annesso(catdoc_padre, catann, descr, tipo, obblig); + fill_annessi(catdoc_padre); + //} } } } + } default: break; } - return true; } @@ -102,9 +167,8 @@ bool TF9_categorie_doc_msk::on_key(KEY key) { TSheet_field& sf = sfield(S_CLASSDOC); sf.hide(); - int row = sf.selected(); TString_array& arr = sf.rows_array(); - arr.destroy(row, true); + arr.destroy(sf.selected(), true); sf.force_update(); sf.show(); } @@ -114,59 +178,23 @@ bool TF9_categorie_doc_msk::on_key(KEY key) void TF9_categorie_doc_msk::check_spell() const { TSheet_field& sf = sfield(S_CLASSDOC); + sf.hide(); FOR_EACH_SHEET_ROW(sf, nr, row) { - TString start; - TString catdoc = start = row->get(F_CATDOC - 101); - catdoc.trim(); - catdoc.upper(); - std::string ss = (const char*)catdoc; - for (size_t i = 0; i < ss.size(); ++i) - { - if (!(ss[i] >= 'A' && ss[i] <= 'Z' || ss[i] >= '0' && ss[i] <= '9' || ss[i] == '_')) - ss.erase(i--, 1); - } - catdoc = ss.c_str(); + TString catdoc = row->get(F_CATDOC - 101); + correct_spell_catdoc(catdoc); row->add(catdoc, 1); - if (catdoc != start) - sf.force_update(); } + sf.force_update(); + sf.show(); } void TF9_categorie_doc_msk::fill_annessi(const TString& catdoc) { - TRecord_categorie annessi(TRecord_categorie::annessi); - TToken_string r; - if (_annessi_sheet == nullptr) - _annessi_sheet = make_unique(-1, -1, 78, 13, "Annessi", "Categoria\nDocumento (Codice)@15|Descrizione@26", 6); - _annessi_sheet->destroy(); - annessi.put("CATDOC", catdoc); - for(bool ok = annessi.read(); ok; ok = annessi.next()) - { - _categorie_doc.put("NAME", annessi.get("NAME")); - _categorie_doc.read(true); - r.cut(0); - r.add(annessi.get("NAME"), 0); - r.add(_categorie_doc.get("DESCR")); - _annessi_sheet->add(r); - } -} - -void TF9_categorie_doc_msk::fill_annessi(const TString_array& annessi) -{ - TToken_string r; - if (_annessi_sheet == nullptr) - _annessi_sheet = make_unique(-1, -1, 78, 13, "Annessi", "Categoria\nDocumento (Codice)@15|Descrizione@26", 6); - _annessi_sheet->destroy(); - FOR_EACH_ARRAY_ITEM(annessi, nr, row) - { - _categorie_doc.put("NAME", ((TToken_string*)row)->get(0)); - _categorie_doc.read(); - r.cut(0); - r.add(((TToken_string*)row)->get(0), 0); - r.add(_categorie_doc.get("DESCR")); - _annessi_sheet->add(r); - } + TCategorie_doc d; + d.set_mode_sheet(6); + _annessi_sheet = d.get_sheet_ann(catdoc); + _annessi_sheet->add_button(DLG_EDIT, TR("~Modifica"), K_CTRL + 'F', TOOL_EDIT); } void TF9_categorie_doc_msk::load_table() const @@ -178,13 +206,12 @@ void TF9_categorie_doc_msk::load_table() const int idx = 0; while (true) { - const TString& appo = ini_get_string(CONFIG_DITTA, "F9", "CATDOC", "", idx++); + TToken_string appo(ini_get_string(CONFIG_DITTA, "F9", "CATDOC", "", idx++)); if (appo == "STOP" || appo.empty()) /* STOP: Riga terminatrice */ break; TToken_string& row = sf.row(-1); row = appo; } - sf.show(); sf.force_update(); } @@ -213,6 +240,21 @@ void TF9_categorie_doc_msk::salva_tabella() const load_table(); } +void TF9_categorie_doc_msk::correct_spell_catdoc(TString& catdoc) +{ + catdoc.trim(); + catdoc.upper(); + std::string ss = (const char*)catdoc; + for (size_t i = 0; i < ss.size(); ++i) + { + if (!(ss[i] >= 'A' && ss[i] <= 'Z' || ss[i] >= '0' && ss[i] <= '9' || ss[i] == '_')) + ss.erase(i--, 1); + } + catdoc = ss.c_str(); +} + + + /////////////////////////////////////////////////////////////// // TF9_categorie_doc @@ -256,7 +298,6 @@ void TF9_categorie_doc::main_loop() DRT_CAUSSO ", " DRT_CAUSCON ", " DRT_TIPOCAU ", " - DRT_TIPOMOV ", " DRT_OPCEE ")\nVALUES('" << F9CONF.get_ambiente() << "', '" << row->get(1) << "', '" << // Catdoc @@ -265,7 +306,6 @@ void TF9_categorie_doc::main_loop() row->get(4) << "', '" << // Causale sost. escape(row->get(5)) << "', '" << // Causale cont escape(row->get(6)) << "', '" << // Tipo caus. cont - escape(row->get(7)) << "', '" << // Tipo mov. row->get(8) << "');\n"; // "Operat. CEE" ok &= fp_db().sq_set_exec(query, false) && fp_db().sq_commit(); if (!ok) diff --git a/src/f9/f90200a.h b/src/f9/f90200a.h index ea03f4a68..b3ff72273 100644 --- a/src/f9/f90200a.h +++ b/src/f9/f90200a.h @@ -8,5 +8,4 @@ #define F_CLASDOCSOS 104 #define F_CAUSSOS 105 #define F_CAUSCONT 106 -#define F_TIPOCAUSCONT 107 -#define F_OPERCEE 108 +#define F_TIPOCAUSCONT 107 \ No newline at end of file diff --git a/src/f9/f90200a.uml b/src/f9/f90200a.uml index 95454906b..b998bcad9 100644 --- a/src/f9/f90200a.uml +++ b/src/f9/f90200a.uml @@ -62,14 +62,13 @@ BEGIN ITEM "Causale per\nSostitutiva (TD01...)@25" ITEM "Causale\nContabile@8" ITEM "Tipo\nDocumento@6" - ITEM "Classificazione\nAnnesso@14" END ENDPAGE ENDMASK -PAGE "Righe doc" -1 -1 78 13 +PAGE "class doc" -1 -1 78 13 BOOLEAN F_SELCLASS BEGIN @@ -133,14 +132,6 @@ BEGIN FLAG "U" END -LIST F_OPERCEE 2 14 -BEGIN - PROMPT 1 8 "Operatore CEE" - ITEM "|" - ITEM "DC|Doc. cartaceo" - ITEM "RC|Reverse Charge" -END - ENDPAGE TOOLBAR "topbar" 0 0 0 2 @@ -159,4 +150,4 @@ BEGIN END ENPAGE -ENDMASK \ No newline at end of file +ENDMASK diff --git a/src/f9/f90300.cpp b/src/f9/f90300.cpp index 9b1ff8f71..dbb480373 100644 --- a/src/f9/f90300.cpp +++ b/src/f9/f90300.cpp @@ -39,6 +39,9 @@ class TImport_msk : public TAutomask const char* select_catdoc(); public: + void set_fields(const TString& impaddress, const TString& impnumreg, bool isannesso = false, const TString& catannpadre = "", + const TString& catdocann = ""); + TImport_msk(); }; @@ -50,9 +53,25 @@ bool TImport_msk::on_field_event(TOperable_field& o, TField_event e, long jolly) if (e == fe_modify) { const bool on = o.mask().get_bool(F_ISANNESSO); - o.mask().enable(F_CATANNPADRE, on); o.mask().enable(F_CATDOCANN, on); } + break; + case F_IMPNUMREG: + if(e == fe_modify) + { + const std::shared_ptr c = categorie_doc().mov2cat(o.get_int()); + if (c != nullptr) + { + set(F_CATANNPADRE, c->catdoc); + set(F_CATDOCANN, ""); + } + else + { + message_box("Questo tipo di registrazione non corrisponde a nessuna delle categorie documentali inserite."); + set(F_CATANNPADRE, ""); + set(F_CATDOCANN, ""); + } + } default: break; } return true; @@ -61,7 +80,8 @@ bool TImport_msk::on_field_event(TOperable_field& o, TField_event e, long jolly) bool TImport_msk::catdocann_handler(TMask_field& field, KEY key) { static const char* msg_notexist = "Non esistono tipologie di annessi per questa categoria documentale."; - static const char* msg_inspadre = "Inserire prima la cat. doc. padre."; + static const char* msg_inspadre = "Questa registrazione non e' riconosciuta in nessuna categoria documentale.\nImpossible inserire un annesso.\n" + "Si prega di inserire prima una cat. documentale che comprenda questo tipo di registrazione\naggiungendo anche eventuali tipologie di annessi."; TImport_msk& msk = (TImport_msk&)field.mask(); if (key == K_TAB && field.dirty() && field.full()) { @@ -149,6 +169,16 @@ const char* TImport_msk::select_catdoc() return ""; } +void TImport_msk::set_fields(const TString& impaddress, const TString& impnumreg, const bool isannesso, + const TString& catannpadre, const TString& catdocann) +{ + set(F_IMPADDRESS, impaddress); + set(F_IMPNUMREG, impnumreg); + set(F_ISANNESSO, isannesso); + set(F_CATANNPADRE, catannpadre); + set(F_CATDOCANN, catdocann); +} + TImport_msk::TImport_msk(): TAutomask("f90300b") { TMask::set_handler(F_CATANNPADRE, catannpadre_handler); @@ -171,10 +201,20 @@ class TGestione_doc_cartacei_f9_msk : public TAutomask TString numreg; TString user; }; + struct ann_cart_t + { + TString numreg; + TString filename; + TString catdocpad; + TString catdocann; + TString loaddate; + TString user; + }; const TString& _addr_cart; // Indirizzo cartella doc. cartacei F9 std::unique_ptr> _extensions; // todo: controllare che con TString funzioni l'ordinamento, quindi la find std::unique_ptr _import_msk; std::map _list_file; + std::map _list_ann; bool on_field_event(TOperable_field& o, TField_event e, long jolly) override; @@ -182,8 +222,10 @@ class TGestione_doc_cartacei_f9_msk : public TAutomask static void check_deleted(); bool check_file_exist(const TString& file) const; void delete_file(); + void edit_ann(); + void edit_file(); void fill(); - void fill_annessi() const; + void fill_annessi(); void fill_docs(); static TToken_string& get_valid_extensions(); void load_extensions(); @@ -215,6 +257,16 @@ bool TGestione_doc_cartacei_f9_msk::on_field_event(TOperable_field& o, TField_ev case B_CONFIG: if (e == fe_button) open_config_win(); + break; + case DLG_USER: + if (e == fe_button) + { + int page = curr_page(); + if (!page) + edit_file(); + else + edit_ann(); + } default: break; } return true; @@ -322,17 +374,88 @@ void TGestione_doc_cartacei_f9_msk::delete_file() fill(); } +void TGestione_doc_cartacei_f9_msk::edit_ann() +{ + TToken_string& row = sfield(S_ANNESSI).row(sfield(S_ANNESSI).selected()); + TImport_msk win; + win.set_caption("Modifica annesso"); + TLocalisamfile f9ann(LF_F9ANNESSI); + const char* numreg = row.get(4); + const char* filename = row.get(1); + f9ann.put(F9A_NUMREG, numreg); + f9ann.put(F9A_FILENAME, filename); + f9ann.read(); + win.set_fields(TF9_doccart::get_full_path_file_cartaceo(filename), numreg, true, f9ann.get(F9A_CATDOCPAD), f9ann.get(F9A_CATDOCANN)); + + win.disable(F_IMPADDRESS); + win.disable(F_ISANNESSO); + win.enable(F_CATDOCANN); + if(win.run() == K_ENTER) + { + if (win.get(F_IMPNUMREG) == numreg) + { + f9ann.put(F9A_CATDOCPAD, win.get(F_CATANNPADRE)); + f9ann.put(F9A_CATDOCANN, win.get(F_CATDOCANN)); + f9ann.write(); + f9ann.rewrite(); + } + else + { + f9ann.remove(); + f9ann.put(F9A_NUMREG, win.get(F_IMPNUMREG)); + f9ann.put(F9A_CATDOCPAD, win.get(F_CATANNPADRE)); + f9ann.put(F9A_CATDOCANN, win.get(F_CATDOCANN)); + f9ann.write(); + f9ann.rewrite(); + } + message_box("Annesso modificato!"); + } + fill_annessi(); +} + +void TGestione_doc_cartacei_f9_msk::edit_file() +{ + TToken_string& row = sfield(S_IMPORTED).row(sfield(S_IMPORTED).selected()); + TImport_msk win; + win.set_caption("Modifica documento cartaceo"); + TLocalisamfile f9docs(LF_F9DOCS); + const char* numreg = row.get(2); + const char* filename = row.get(1); + f9docs.put(F9C_NUMREG, numreg); + f9docs.read(); + win.set_fields(TF9_doccart::get_full_path_file_cartaceo(filename), numreg); + + win.disable(F_IMPADDRESS); + win.disable(F_ISANNESSO); + if (win.run() == K_ENTER && win.get(F_IMPNUMREG) != numreg) + { + f9docs.remove(); + f9docs.put(F9C_NUMREG, win.get(F_IMPNUMREG)); + f9docs.write(); + f9docs.rewrite(); + message_box("Documento modificato!"); + } + fill_docs(); +} + void TGestione_doc_cartacei_f9_msk::fill() { fill_docs(); fill_annessi(); } -void TGestione_doc_cartacei_f9_msk::fill_annessi() const +void TGestione_doc_cartacei_f9_msk::fill_annessi() { + std::set file_err; + TLocalisamfile ann(LF_F9ANNESSI); if(ann.first() == NOERR) { + const TString& namefile = ann.get(F9A_FILENAME); + const bool not_exist = !check_file_exist(namefile); + if (not_exist) + file_err.insert(namefile); + TLocalisamfile mov(LF_MOV); TSheet_field& sf = sfield(S_ANNESSI); sf.hide(); @@ -345,21 +468,43 @@ void TGestione_doc_cartacei_f9_msk::fill_annessi() const mov.put(MOV_NUMREG, numreg); mov.read(); - row.add(ann.get(F9A_FILENAME), 1); // ITEM "Nome File@25" - row.add(ann.get(F9A_CATDOCPAD)); // ITEM "Categoria di\nappartenenza@7" - row.add(ann.get(F9A_CATDOCANN)); // ITEM "Classe doc.\nannesso@7" - row.add(numreg); // ITEM "Movimento\nContabile N.@7" - row.add(mov.get(MOV_DATAREG)); // ITEM "Data\nMovimento@10" - row.add(mov.get(MOV_CODCAUS)); // ITEM "Causale@7" - row.add(mov.get(MOV_NUMDOC)); // ITEM "Num. Doc.@7" + row.add(ann.get(F9A_FILENAME), 1); // ITEM "Nome File@25" + row.add(ann.get(F9A_CATDOCPAD)); // ITEM "Categoria di\nappartenenza@7" + row.add(ann.get(F9A_CATDOCANN)); // ITEM "Classe doc.\nannesso@7" + row.add(numreg); // ITEM "Movimento\nContabile N.@7" + row.add(mov.get(MOV_DATAREG)); // ITEM "Data\nMovimento@10" + row.add(mov.get(MOV_CODCAUS)); // ITEM "Causale@7" + row.add(mov.get(MOV_NUMDOC)); // ITEM "Num. Doc.@7" row.add(TString(mov.get(MOV_REG)) << "/" << mov.get(MOV_PROTIVA)); // ITEM "Reg./Prot. IVA@10" - row.add(mov.get(MOV_DESCR)); // ITEM "Descrizione@20" - row.add(ann.get(F9A_LOADDATE)); // ITEM "Data\nCaricamento@10" - // todo: row.add("") // ITEM "Info (Errori)@25" + row.add(mov.get(MOV_DESCR)); // ITEM "Descrizione@20" + row.add(ann.get(F9A_LOADDATE)); // ITEM "Data\nCaricamento@10" + row.add(not_exist ? "IL FILE NON E' PRESENTE IN ARCHIVIO." : ""); // ITEM "Info (Errori)@25" + + _list_ann.insert({ namefile, { numreg, namefile, ann.get(F9A_CATDOCPAD), ann.get(F9A_CATDOCANN), ann.get(F9C_LOADDATE), ann.get(F9C_USER) } }); } while (ann.next() == NOERR); sf.force_update(); sf.show(); } + + if (!file_err.empty()) + { + int count = 0; + const bool m = (int)file_err.size() == 1; + const char p = m ? 'o' : 'i'; + TString msg; msg << (int)file_err.size() << " file non " << (m ? "e'" : "sono") << " stat" << p << + " trovat" << p << ".\nSi prega di rimuoverl" << p << " dall'elenco\n\nFile non trovat" << p << ":"; + + for (auto it = file_err.begin(); it != file_err.end(); ++it) + { + if (count++ == 10) + { + msg << "\n" << "..."; + break; + } + msg << "\n" << *it << " [Num. Reg.: " << _list_ann.find(*it)->second.numreg << "]"; + } + const bool del = warning_box(msg); + } } void TGestione_doc_cartacei_f9_msk::fill_docs() @@ -427,13 +572,13 @@ void TGestione_doc_cartacei_f9_msk::fill_docs() msg << "\n" << *it << " [Num. Reg.: " << _list_file.find(*it)->second.numreg << "]"; } #ifndef DBG - const bool del = warning_box(msg); + const bool del = warning_box(msg); #else - if (yesno_box(msg)) - { - remove_file_from_f9cart(file_err); - fill(); - } + if (yesno_box(msg)) + { + remove_file_from_f9cart(file_err); + fill(); + } #endif } } diff --git a/src/f9/f90300a.uml b/src/f9/f90300a.uml index ccc9af179..eb89463d1 100644 --- a/src/f9/f90300a.uml +++ b/src/f9/f90300a.uml @@ -172,8 +172,27 @@ END ENDPAGE + +TOOLBAR "topbar" 0 0 0 2 + +BUTTON DLG_USER 2 2 +BEGIN + PROMPT 1 1 "Annessi" + PICTURE TOOL_LINK +END + +BUTTON DLG_QUIT 2 2 +BEGIN + PROMPT 2 1 "Fine" + MESSAGE EXIT,K_QUIT + PICTURE TOOL_QUIT +END + +ENPAGE ENDMASK + + PAGE "ANNESSO" -1 -1 78 13 BOOLEAN F_ASEL @@ -250,4 +269,21 @@ END ENDPAGE + +TOOLBAR "topbar" 0 0 0 2 + +BUTTON DLG_USER 2 2 +BEGIN + PROMPT 1 1 "Annessi" + PICTURE TOOL_LINK +END + +BUTTON DLG_QUIT 2 2 +BEGIN + PROMPT 2 1 "Fine" + MESSAGE EXIT,K_QUIT + PICTURE TOOL_QUIT +END + +ENPAGE ENDMASK \ No newline at end of file diff --git a/src/f9/f90300b.uml b/src/f9/f90300b.uml index 30627eccc..bd221a798 100644 --- a/src/f9/f90300b.uml +++ b/src/f9/f90300b.uml @@ -44,7 +44,7 @@ END STRING F_CATANNPADRE 10 BEGIN PROMPT 2 5 "Cat.doc. padre" - FLAGS "BD" + FLAGS "D" END STRING F_CATDOCANN 10 diff --git a/src/f9/f90400.cpp b/src/f9/f90400.cpp index 015184792..9d7253fd0 100644 --- a/src/f9/f90400.cpp +++ b/src/f9/f90400.cpp @@ -1,8 +1,14 @@ +#include "f90.h" + #include "annessif9.h" #include "applicat.h" -#include "f90.h" #include "f9lib01.h" + +/** + * \brief Some test for F9 classes + */ + class TF9_test_app : public TSkeleton_application { public: @@ -17,38 +23,18 @@ void TF9_test_app::main_loop() f.zero(); f.setkey(1); f.put(F9A_NUMREG, search_numreg); - + TString filename = f.get(F9A_FILENAME); TString numreg; - for(f.read(_isgteq); (numreg = f.get(F9A_NUMREG)) == search_numreg; f.next()) + for (f.read(_isgteq); (numreg = f.get(F9A_NUMREG)) == search_numreg; f.next()) { - filename = f.get(F9A_FILENAME); - numreg = f.get(F9A_NUMREG); + filename = f.get(F9A_FILENAME); + numreg = f.get(F9A_NUMREG); //message_box("Filename: %s\nNumreg: %s", (const char*)filename, (const char*)numreg); CHECK(numreg == search_numreg, "This shit sucks."); CHECK(filename == "annmid.pdf" || filename == "annmid2.pdf", "YOU'RE WRONG"); } - // TISAM_recordset FAILS. A sto punto uso il primo. - //TISAM_recordset sta(R"(USE ANNESSIF9 FROM NUMREG=="95223" TO NUMREG=="95223")"); - //for (bool ok = sta.move_first(); ok; ok = sta.move_next()) - //{ - // TString filename = sta.get(F9A_FILENAME).as_string(); - // TString numreg = sta.get(F9A_NUMREG).as_string(); - // CHECK(numreg == "95223", "isam recordset failed. (EOF)"); // FAILS! - // bool simo = true; - //} - - // FAILS - //TF9_doccart doccart; - ////const TDate domani("23-05-2020"); - //doccart.put_in_doc(F9C_NUMREG, "95755"); - //bool ok = doccart.read_doc(); - //filename = doccart.get_in_doc(F9C_FILENAME); - //numreg = doccart.get_in_doc(F9C_NUMREG); - //CHECK(numreg == "95755", "Error in line"); - //TDate loaddate(doccart.get_in_doc(F9F_LOADDATE)); - TF9_doccart d; TString_array sa; CHECK(d.mov2listann(search_numreg, sa) && sa.items() == 2, "COSA STAI LEGGENDO??"); @@ -60,16 +46,115 @@ void TF9_test_app::main_loop() ann.put(F9A_FILENAME, namefile); CHECK(ann.read() == NOERR, "Non esiste questo numreg??"); filename = ann.get(F9A_FILENAME); - TString rec; rec << - filename << "\n" << - ann.get(F9A_NUMREG) << "\n" << + TString rec; rec << + filename << "\n" << + ann.get(F9A_NUMREG) << "\n" << ann.get(F9A_CATDOCPAD) << "\n" << ann.get(F9A_CATDOCANN) << "\n" << - ann.get(F9A_LOADDATE) << "\n" << + ann.get(F9A_LOADDATE) << "\n" << ann.get(F9A_USER); CHECK(filename == "annmid.pdf" || filename == "annmid2.pdf", "YOU'RE WRONG"); //message_box(rec); } + + ///////////////////////// + // Preload some cat doc. + if (noyes_box("Attenzione: procedere all'eliminazione e caricamento di categorie documentali e annessi di prova?")) + { + TCategorie_doc preload; + preload.remove_all(); + preload.reload(); + preload.add_categoria("FATTACQ", "FATTURE DI ACQUISTI", "FTA", "TD01", "", "FA"); + preload.add_categoria("FATTVEN", "FATTURE DI VENDITA", "FTV", "TD01", "", "FV"); + preload.add_categoria("NOTCREDACQ", "Nota credito di acquisto", "FTA", "TD04", "", "NC"); + preload.add_categoria("NOTDEBVEN", "Nota debito di vendita", "FTV", "TD05", "", "ND"); + preload.add_categoria("FATTCORR", "Fattura corrispettivi", "FTA", "TD01", "", "CR"); + preload.add_categoria("ACQREV", "Fattura acq. con rev. charge", "FTA", "TD01", "052", "FA"); + + preload.add_annesso("FATTACQ", "GENACQ", "Annesso generico acq", "DC"); + preload.add_annesso("FATTVEN", "GENVEN", "Annesso gen. ven.", "DC"); + preload.add_annesso("ACQREV", "INTREVC", "Prosp. integr. rev.ch.", "RC", true); + preload.add_annesso("FATTACQ", "LEASING", "Annesso leasing", "DC"); + + CHECK(preload.get_array_rows().items() > 0, "Errore aggiunta o caricamento categorie documentali."); + TCategorie_doc::annesso annesso; + bool get_ann = preload.get_ann("GENACQ", annesso); + TCategorie_doc::classe_doc get_classe_doc_right{ "ACQREV", "Fattura acq. con rev. charge", "FTA", "TD01", "052", "FA" }; + std::shared_ptr get_classe_doc = preload.get_classe_doc("ACQREV"); + CHECK(get_classe_doc->catdoc == get_classe_doc_right.catdoc && + get_classe_doc->descr == get_classe_doc_right.descr && + get_classe_doc->class_sost == get_classe_doc_right.class_sost && + get_classe_doc->caus_sost == get_classe_doc_right.caus_sost && + get_classe_doc->causcont == get_classe_doc_right.causcont && + get_classe_doc->tipocaus == get_classe_doc_right.tipocaus, "get_classe_doc failed" + ); + std::set get_name_catdocs = preload.get_name_catdocs(); + CHECK(get_name_catdocs.find("FATTACQ") != get_name_catdocs.end() && + get_name_catdocs.find("FATTVEN") != get_name_catdocs.end() && + get_name_catdocs.find("NOTCREDACQ") != get_name_catdocs.end() && + get_name_catdocs.find("NOTDEBVEN") != get_name_catdocs.end() && + get_name_catdocs.find("FATTCORR") != get_name_catdocs.end() && + get_name_catdocs.find("ACQREV") != get_name_catdocs.end(), "Get catdocs failed" + ); + std::shared_ptr mov2cat = preload.mov2cat(95752); + CHECK(mov2cat, "mov2cat failed: classe doc is null"); + mov2cat = preload.mov2cat(96955); // Rev. charge causale 052 + CHECK(mov2cat->causcont == "052", "Error retriving cat.doc. from numreg for causale."); + + const TString cat_selected = "FATTACQ"; + TCategorie_doc catdoc; + std::shared_ptr annessi = catdoc.get_sheet_ann(cat_selected); + CHECK(annessi->items() == 2, "TCategorie_doc::get_sheet_ann() failed: retrived %d elements instead of 2", (int)annessi->items()); + + std::shared_ptr sheet_catdocs = catdoc.get_sheet_catdocs(); + CHECK(sheet_catdocs->items() == 6, "TCategorie_doc::get_sheet_catdocs() failed: retrived %d elements instead of 6", (int)sheet_catdocs->items()); + + + TRecord_categorie rec(TRecord_categorie::catdocs); + TRecord_categorie rec2(TRecord_categorie::annessi); + rec.put("NAME", "FATTACQ"); + rec.read(); + TString name = rec.get("NAME"); + TString descr = rec.get("DESCR"); + TString classdo = rec.get("CLASSDOCSOS"); + TString caussos = rec.get("CAUSSOS"); + TString causcon = rec.get("CAUSCONT"); + TString tipodoc = rec.get("TIPODOC"); + CHECK(name == "FATTACQ" && descr.starts_with("FATTURE") && classdo == "FTA" && caussos == "TD01"&& causcon.blank() && tipodoc == "FA", "TRecord_categorie failed to retrieve FATTACQ"); + rec2.put("CATDOC", "FATTACQ"); + bool ok = rec2.read(); + CHECK(ok, "TRecord_categorie (annessi) failed to retrieve annessi FATTACQ"); + for (; ok; ok = rec2.next()) + { + TString s_catdoc = rec2.get("CATDOC"); + TString s_name = rec2.get("NAME"); + TString s_descr = rec2.get("DESCR"); + TString s_tipoann = rec2.get("TIPOANN"); + bool s_obblig = rec2.get("OBBLIG") == "X"; + CHECK(s_catdoc == "FATTACQ" && s_tipoann == "DC" && !s_obblig && (s_name == "GENACQ" && s_descr.starts_with("Annesso generico") || + s_name == "LEASING" && s_descr.starts_with("Annesso lea")), + "TRecord_categorie (annessi) failed to retrive annessi FATTACQ"); + } + } + + TF9_doccart doccart; + TString old_numreg; old_numreg << 95752; + TFilename doc; + TString_array list_annessi; + vector vect_annessi; + //TFilename f_doc; f_doc << "asd"; + //bool annesso; + //bool doc_ae = doccart.doc_already_exists(f_doc, old_numreg, annesso); + bool movdoc = doccart.mov2doc(old_numreg, doc); + TString msg("Error retriving filename from reg n. "); msg << old_numreg; + CHECK(movdoc && TString(doc.name()) == "ve1300_F01_0000000011_0002237.pdf", (const char*)msg); + + old_numreg.cut(0) << 95222; // Doppio annesso + bool movann = doccart.mov2listann(old_numreg, list_annessi); + CHECK(movann && list_annessi.items() == 2, "Error retriving list annessi from numreg %s", (const char*)old_numreg); + bool movannvect = doccart.mov2listann_vect(old_numreg, vect_annessi); + CHECK(movannvect && vect_annessi.size() == 2, "Error retriving vector annessi from numreg %s", (const char*)old_numreg); + message_box("TESTS COMPLETELY SUCCESSFUL"); } diff --git a/src/f9/f9lib01.cpp b/src/f9/f9lib01.cpp index 56f7de279..0e748613c 100644 --- a/src/f9/f9lib01.cpp +++ b/src/f9/f9lib01.cpp @@ -41,6 +41,62 @@ const char* TEstrazione::caus_sos(const TLocalisamfile& mov, const TipoIVA iva) return ""; } +void TEstrazione::check_annessi(movimento_t& mov_i, const TString& numreg) +{ + TToken_string ann_nexist; + if (!check_annessi_oblig(mov_i.catdoc->catdoc, numreg, ann_nexist)) + { + TString msg_annessi_mancanti("Annessi obligatori mancanti: "); + for (int i = 0; i < ann_nexist.items(); ++i) + msg_annessi_mancanti << ann_nexist.get() << " "; + mov_i.err = true; + mov_i.estratto = false; + mov_i.descr_err = msg_annessi_mancanti; + mov_i.descr_estr = movimento_t::annesso_nexist; + } + else + { + const bool loaded = load_annessi(mov_i); +#ifdef DBG + if (loaded) + bool simo = true; +#endif + } +} + +bool TEstrazione::check_annessi_oblig(const TString& catdoc, const TString& numreg, TToken_string& ann_nexist) +{ + ann_nexist.destroy(-1); + TCategorie_doc categorie; + const TString_array lista_cat_annessi = categorie.get_array_ann(catdoc); // Lista cat annessi + TF9_doccart file_cart; + std::vector list_file_ann; // Lista file annessi + file_cart.mov2listann_vect(numreg, list_file_ann); + bool ok_ann = true; + FOR_EACH_ARRAY_ITEM(lista_cat_annessi, nr, ann) + { + TCategorie_doc::annesso annesso; + const bool ok_cat = categorie.get_ann(*(TToken_string*)ann, annesso); + if (ok_cat && annesso.obblig) + { + // Controllo che esista l'annesso per questo mov. + bool exist = false; + for (auto f_ann = list_file_ann.begin(); f_ann != list_file_ann.end(); ++f_ann) + { + if (f_ann->catdocann == *(TToken_string*)ann) + { + exist = true; + break; + } + } + ok_ann &= exist; + if (!exist) + ann_nexist.add(*(TToken_string*)ann); + } + } + return ok_ann; +} + bool TEstrazione::check_documento_vendita(const TLocalisamfile& mov, _Out_ bool& exist_doc) { if (!mov.get(MOV_DPROVV).empty() && !mov.get(MOV_DANNO).empty() && !mov.get(MOV_DCODNUM).empty() && !mov.get(MOV_DNDOC).empty()) @@ -421,6 +477,9 @@ const char* TEstrazione::diagnostica_mov() mov_i.estratto = cd.get(); mov_i.descr_estr = cd ? movimento_t::no_err : movimento_t::no_catdoc; mov_i.catdoc = cd; + + if (mov_i.catdoc) + check_annessi(mov_i, numreg); } if (mov_i.estratto) { @@ -442,11 +501,6 @@ const char* TEstrazione::diagnostica_mov() mov_i.descr_estr = movimento_t::no_filecart; } } - bool loaded = load_annessi(mov_i); -#ifdef DBG - if (loaded) - bool simo = true; -#endif } } ok &= mov_i.err; @@ -477,8 +531,10 @@ const char* TEstrazione::diagnostica_mov() bool simo = true; // Rev charge if (it->numreg == 95752) bool simo = true; // Cartaceo - if (it->numreg == 95740) // Annessi - bool simo = true; + if (it->numreg == 95740) + bool simo = true; // Annessi + if (it->numreg == 95222) + bool simo = true; // Annessi #endif /* Controlli per vendite cambiati: * Elettroniche solo quelle agli italiani, tutti gli altri sono cartacei @@ -521,6 +577,9 @@ const char* TEstrazione::diagnostica_mov() mov_i.descr_estr = cd ? movimento_t::no_err : movimento_t::no_catdoc; mov_i.catdoc = cd; + if (mov_i.catdoc) + check_annessi(mov_i, numreg); + if (!mov_i.catdoc) ++_stats.fv_nocatdoc; if (mov_i.err) @@ -547,11 +606,6 @@ const char* TEstrazione::diagnostica_mov() mov_i.descr_estr = movimento_t::no_filecart; } } - bool loaded = load_annessi(mov_i); -#ifdef DBG - if (loaded) - bool simo = true; -#endif } ok &= !mov_i.err; } @@ -718,61 +772,54 @@ bool TEstrazione::estrazione_iva(bool escluso) { vector& ann = it->annessi; TCategorie_doc cd; - auto classost = [&](const int i) - { - const std::shared_ptr as = cd.get_classe_doc(ann[i].catdocann); - if (as != nullptr) - return as->class_sost; - return TString(""); - }; size_t i = 0; size_t size = ann.size(); - iva_query.add(IVA_CLASAN1, classost(i)); - iva_query.add(IVA_NOMF1, ann[i++].filename); + iva_query.add(IVA_CLASAN1, ann[i].catdocann); + iva_query.add(IVA_NOMF1, ann[i++].filename); // HOW DID MY LIFE COME TO THIS?.... if (size > i) { - iva_query.add(IVA_CLASAN2, classost(i)); + iva_query.add(IVA_CLASAN2, ann[i].catdocann); iva_query.add(IVA_NOMF2, ann[i++].filename); } if (size > i) { - iva_query.add(IVA_CLASAN3, classost(i)); + iva_query.add(IVA_CLASAN3, ann[i].catdocann); iva_query.add(IVA_NOMF3, ann[i++].filename); } if (size > i) { - iva_query.add(IVA_CLASAN4, classost(i)); + iva_query.add(IVA_CLASAN4, ann[i].catdocann); iva_query.add(IVA_NOMF4, ann[i++].filename); } if (size > i) { - iva_query.add(IVA_CLASAN5, classost(i)); + iva_query.add(IVA_CLASAN5, ann[i].catdocann); iva_query.add(IVA_NOMF5, ann[i++].filename); } if (size > i) { - iva_query.add(IVA_CLASAN4, classost(i)); + iva_query.add(IVA_CLASAN4, ann[i].catdocann); iva_query.add(IVA_NOMF4, ann[i++].filename); } if (size > i) { - iva_query.add(IVA_CLASAN6, classost(i)); + iva_query.add(IVA_CLASAN6, ann[i].catdocann); iva_query.add(IVA_NOMF6, ann[i++].filename); } if (size > i) { - iva_query.add(IVA_CLASAN7, classost(i)); + iva_query.add(IVA_CLASAN7, ann[i].catdocann); iva_query.add(IVA_NOMF7, ann[i++].filename); } if (size > i) { - iva_query.add(IVA_CLASAN8, classost(i)); + iva_query.add(IVA_CLASAN8, ann[i].catdocann); iva_query.add(IVA_NOMF8, ann[i++].filename); } if (size > i) { - iva_query.add(IVA_CLASAN9, classost(i)); + iva_query.add(IVA_CLASAN9, ann[i].catdocann); iva_query.add(IVA_NOMF9, ann[i++].filename); } } @@ -1002,22 +1049,12 @@ std::shared_ptr TCategorie_doc::find_tipodoc(const T for (auto it = _rows.begin(); it != _rows.end(); ++it) { const std::shared_ptr& classe = *it; - if (classe->causcont.empty() && classe->tipocaus == tipodoc) + if (classe->causcont.blank() && classe->tipocaus == tipodoc) return *it; } return nullptr; } -std::vector>::iterator TCategorie_doc::find_annesso(const TString& catdoc, const char* catdoc_ann) -{ - for (auto it = _annessi.begin(); it != _annessi.end(); ++it) - { - if (it->first == catdoc && it->second == catdoc_ann) - return it; - } - return _annessi.end(); -} - std::vector>>::iterator TCategorie_doc::find_sheet_annessi(const TString& catdoc) { for(auto it = _sheets_annessi.begin(); it != _sheets_annessi.end(); ++it) @@ -1041,15 +1078,16 @@ std::shared_ptr TCategorie_doc::get_classe_doc(const void TCategorie_doc::load_all() { _rows.clear(); - _annessi.clear(); + _rows_annessi.clear(); int idx = 0; while (true) { const TString& appo = ini_get_string(CONFIG_DITTA, INI_PAR_MOD, INI_CATDOC, "", idx++); - if (appo == "STOP" || appo.empty()) /* STOP: Riga terminatrice */ + if (appo == "STOP" || appo.blank()) /* STOP: Riga terminatrice */ break; TToken_string row(appo); - classe_doc cd = { row.get(1), row.get(), row.get(), row.get(), row.get(), row.get(), row.get(), row.get() }; + classe_doc cd = { row.get(1), row.get(), row.get(), row.get(), row.get(), row.get() }; + _rows.emplace_back(std::make_shared(cd)); } @@ -1057,13 +1095,66 @@ void TCategorie_doc::load_all() while (true) { const TString& appo = ini_get_string(CONFIG_DITTA, INI_PAR_MOD, INI_ANNESSI, "", idx++); - if (appo == "STOP" || appo.empty()) /* STOP: Riga terminatrice */ + if (appo == "STOP" || appo.blank()) /* STOP: Riga terminatrice */ break; TToken_string row(appo); - _annessi.emplace_back(pair({ row.get(0), row.get(1) })); + annesso ann{ row.get(0), row.get(), row.get(), row.get(), row.get_bool() }; + _rows_annessi.insert({ row.get(1), ann }); } } +void TCategorie_doc::save_ann() +{ + remove_all_ann(); + + int idx = 0; + for (auto it = _rows_annessi.begin(); it != _rows_annessi.end(); ++it) + { + TToken_string row; + if(!it->second.catdocpadre.blank()) + row.add(it->second.catdocpadre, 0); + if (!it->second.catdoc.blank()) + row.add(it->second.catdoc, 1); + if (!it->second.descr.blank()) + row.add(it->second.descr, 2); + if (!it->second.opcee.blank()) + row.add(it->second.opcee, 3); + row.add(it->second.obblig ? "X" : "", 4); + ini_set_string(CONFIG_DITTA, INI_PAR_MOD, INI_ANNESSI, row, idx++); + } + ini_set_string(CONFIG_DITTA, INI_PAR_MOD, INI_ANNESSI, "STOP", idx); // Riga terminatrice + + reload(); +} + +void TCategorie_doc::save_cat() +{ + remove_all_cat(); + + int idx = 0; + for (auto it = _rows.begin(); it != _rows.end(); ++it) + { + classe_doc& cd = **it; + TToken_string row; + if (!cd.catdoc.blank()) + row.add(cd.catdoc, 1); + if (!cd.descr.blank()) + row.add(cd.descr, 2); + if (!cd.class_sost.blank()) + row.add(cd.class_sost, 3); + if (!cd.caus_sost.blank()) + row.add(cd.caus_sost, 4); + if (!cd.causcont.blank()) + row.add(cd.causcont, 5); + if (!cd.tipocaus.blank()) + row.add(cd.tipocaus, 6); + ini_set_string(CONFIG_DITTA, INI_PAR_MOD, INI_CATDOC, row, idx++); + } + ini_set_string(CONFIG_DITTA, INI_PAR_MOD, INI_CATDOC, "STOP", idx); // Riga terminatrice + + reload(); +} + const char* TCategorie_doc::traduci_caus_sost(const TString& caus) { if (caus == "TD01") return "TD01 Fattura"; @@ -1079,7 +1170,7 @@ const char* TCategorie_doc::traduci_caus_sost(const TString& caus) const char* TCategorie_doc::traduci_class_ann(const TString& class_ann) { if (class_ann == "RC") return "Reverse Charge"; - if (class_ann == "DC") return "Doc. cartaceo"; + if (class_ann == "DC") return "ANN. cartaceo"; return ""; } @@ -1090,75 +1181,74 @@ const char* TCategorie_doc::traduci_class_sost(const TString& class_sost) return ""; } -void TCategorie_doc::add_annesso(const TString& catdoc, const char* catdoc_ann) +std::map::iterator TCategorie_doc::find_annesso(const TString& catdoc_padre, const char* catdoc_ann) { - _annessi.insert(_annessi.end(), pair({ catdoc, catdoc_ann })); + auto it = _rows_annessi.find(catdoc_ann); + if (it->second.catdocpadre == catdoc_padre) + return it; + return _rows_annessi.end(); +} - int idx = 0; - TString iget = "start"; - while (iget != "STOP" && !iget.empty()) - { - iget = ini_get_string(CONFIG_DITTA, INI_PAR_MOD, INI_ANNESSI, "", idx); - ini_remove(CONFIG_DITTA, INI_PAR_MOD, INI_ANNESSI, idx++); - } +void TCategorie_doc::add_annesso(const TString& catdoc_padre, const TString& catdoc_ann, const TString& descr, + const TString& class_ann, const bool obblig) +{ +#ifdef DBG + CHECK(catdoc_padre && *catdoc_padre && catdoc_ann && *catdoc_ann && class_ann && *class_ann, + "add_annesso failed: some parameters are NULL or keys are empty"); +#else + if (catdoc_padre && *catdoc_padre && catdoc_ann && *catdoc_ann && class_ann && *class_ann) + return; +#endif + annesso ann{ catdoc_padre, catdoc_ann, descr, class_ann, obblig }; + _rows_annessi.insert({ catdoc_padre, ann }); - idx = 0; - for(auto it = _annessi.begin(); it != _annessi.end(); ++it) - { - TToken_string row; - row.add(it->first, 0); - row.add(it->second); - ini_set_string(CONFIG_DITTA, INI_PAR_MOD, INI_ANNESSI, row, idx++); - } - ini_set_string(CONFIG_DITTA, INI_PAR_MOD, INI_ANNESSI, "STOP", idx); // Riga terminatrice + save_ann(); +} - reload(); +void TCategorie_doc::add_categoria(const TString& catdoc, const TString& descr, const TString& class_sost, const TString& caus_sost, + const TString& causcont, const TString& tipocaus) +{ + classe_doc cd = { catdoc, descr, class_sost, caus_sost, causcont, tipocaus }; + _rows.emplace_back(std::make_shared(cd)); + save_cat(); } void TCategorie_doc::del_annesso(const TString& catdoc, const char* catdoc_ann) { - const auto annesso = find_annesso(catdoc, catdoc_ann); - if(annesso != _annessi.end()) + const auto ann = find_annesso(catdoc, catdoc_ann); + if(ann != _rows_annessi.end()) { - _annessi.erase(annesso); - - int idx = 0; - TString iget = "start"; - while (iget != "STOP" && !iget.empty()) - { - iget = ini_get_string(CONFIG_DITTA, INI_PAR_MOD, INI_ANNESSI, "", idx); - ini_remove(CONFIG_DITTA, INI_PAR_MOD, INI_ANNESSI, idx++); - } - - idx = 0; - for (auto it = _annessi.begin(); it != _annessi.end(); ++it) - { - TToken_string row; - row.add(it->first, 0); - row.add(it->second); - ini_set_string(CONFIG_DITTA, INI_PAR_MOD, INI_ANNESSI, row, idx++); - } - ini_set_string(CONFIG_DITTA, INI_PAR_MOD, INI_ANNESSI, "STOP", idx); // Riga terminatrice - - reload(); + _rows_annessi.erase(ann); + save_ann(); } } +bool TCategorie_doc::get_ann(const TString& catann, annesso& annesso) +{ + const map::iterator it = _rows_annessi.find(catann); + if (it != _rows_annessi.end()) + { + annesso = it->second; + return true; + } + return false; +} + TString_array TCategorie_doc::get_array_ann(const TString& catdoc) { TString_array sa(0); - for(auto it = _annessi.begin(); it != _annessi.end(); ++it) + for(auto it = _rows_annessi.begin(); it != _rows_annessi.end(); ++it) { - if (it->first == catdoc) + if (it->second.catdocpadre == catdoc) { - TToken_string row(it->second); + TToken_string row(it->second.catdoc); sa.add(row); } } return sa; } -TString_array TCategorie_doc::get_array_rows(bool traduci) +TString_array TCategorie_doc::get_array_rows(const bool traduci) { TString_array sa(_rows.size()); for(auto it = _rows.begin(); it != _rows.end(); ++it) @@ -1171,7 +1261,6 @@ TString_array TCategorie_doc::get_array_rows(bool traduci) ts.add(!traduci ? row->caus_sost : traduci_caus_sost(row->caus_sost)); ts.add(row->causcont); ts.add(row->tipocaus); - ts.add(!traduci ? row->tipomov : traduci_class_ann(row->tipomov)); sa.add(ts); } @@ -1182,17 +1271,17 @@ std::shared_ptr TCategorie_doc::get_sheet_catdocs() { if (_sheet_catdocs == nullptr) { - _sheet_catdocs = make_shared(-1, -1, 78, 13, "cat.docs", + _sheet_catdocs = make_shared(-1, -1, 125, 20, "Categorie Documentali", "Categoria\nDocumento(Codice)@15|Descrizione\nDocumento@26|" - "Classe Documentale\nSostitutiva@25|Causale per\nSostitutiva (TD01...)@34|" - "Causale\nContabile@8|Tipo\nDocumento@6|Classificazione\nAnnesso@15", + "Classe Documentale\nSostitutiva@13|Causale per\nSostitutiva (TD01...)@13|" + "Causale\nContabile@8|Tipo\nDocumento@8", MODE_SHEETS); if (!_name_catdocs) _name_catdocs = make_shared>(); else _name_catdocs->clear(); //_sheet_catdocs->add_button(DLG_CANCEL, "Annulla", K_ESC, TOOL_CANCEL, TOOL_CANCEL); - const TString_array ar = get_array_rows(true); + const TString_array ar = get_array_rows(); FOR_EACH_ARRAY_ITEM(ar, nr, row) { _sheet_catdocs->add(*(TToken_string*)row); @@ -1207,30 +1296,32 @@ std::shared_ptr TCategorie_doc::get_sheet_ann(const TString& catdo auto it = find_sheet_annessi(catdoc); if (it == _sheets_annessi.end()) { - const auto inserted = _sheets_annessi.insert(_sheets_annessi.end(), { - catdoc, make_shared(-1, -1, 78, 13, "Annessi", - "Categoria\nAnnesso(Codice)@15|Descrizione\nDocumento@26|" - "Classe Documentale\nSostitutiva@25|Classificazione\nAnnesso@14", - MODE_SHEETS) - }); + TString title; title << "Annessi della Categoria " << catdoc; + shared_ptr sheet = make_shared(-1, -1, 78, 13, title, + "Categoria\nAnnesso(Codice)@15|Descrizione\nDocumento@26|" + "Classificazione\nAnnesso@14|Obbligatorio@8", + _mode_sheet); + const auto inserted = _sheets_annessi.insert(_sheets_annessi.end(), { catdoc, sheet }); const TString_array aann = get_array_ann(catdoc); if (!aann.empty()) { - TArray_sheet* as = inserted->second.get(); - TRecord_categorie rec; - FOR_EACH_ARRAY_ITEM(aann, nr, row) + FOR_EACH_ARRAY_ITEM(aann, nr, r) { - rec.put("NAME", ((TToken_string*)row)->get(0)); - rec.read(true); - TToken_string t; - t.add(((TToken_string*)row)->get(0)); - t.add(rec.get("DESCR")); - t.add(rec.get("CLASSDOCSOS")); - t.add(rec.get("CLASSANN")); - as->add(t); + TToken_string& row = *(TToken_string*)r; + std::map::iterator it_ann = find_annesso(catdoc, row.get(0)); + if(it_ann != _rows_annessi.end()) + { + const annesso& ann = it_ann->second; + TToken_string t; + t.add(ann.catdoc); + t.add(ann.descr); + t.add(ann.opcee); + t.add(ann.obblig ? "X" : ""); + sheet->add(t); + } } } - it = inserted; + it = inserted; // Per restituirlo anche vuoto. } return it->second; } @@ -1260,6 +1351,39 @@ void TCategorie_doc::reload() load_all(); } +void TCategorie_doc::remove_all() +{ + remove_all_ann(); + remove_all_cat(); +} + +void TCategorie_doc::remove_all_ann() +{ + int idx = 0; + TString iget = "start"; + while (iget != "STOP" && !iget.blank()) + { + iget = ini_get_string(CONFIG_DITTA, INI_PAR_MOD, INI_ANNESSI, "", idx); + ini_remove(CONFIG_DITTA, INI_PAR_MOD, INI_ANNESSI, idx++); + } +} + +void TCategorie_doc::remove_all_cat() +{ + int idx = 0; + TString iget = "start"; + while (iget != "STOP" && !iget.blank()) + { + iget = ini_get_string(CONFIG_DITTA, INI_PAR_MOD, INI_CATDOC, "", idx); + ini_remove(CONFIG_DITTA, INI_PAR_MOD, INI_CATDOC, idx++); + } +} + +TCategorie_doc::TCategorie_doc() : _mode_sheet(MODE_SHEETS) +{ + load_all(); +} + /////////////////////////////////////////////////////////////////////////////// // TRecord_categorie /////////////////////////////////////////////////////////////////////////////// @@ -1312,12 +1436,19 @@ bool TRecord_categorie::read(bool traduci) FOR_EACH_ARRAY_ITEM(sa, nr, str) { TString n = (*(TToken_string*)str).get(0); - if (name.empty() || n == name) + if (name.blank() || n == name) // Se name vuoto perche' sto cercando solo per catdoc { - TToken_string t; - t.add(catdoc); - t.add(n); - _result_set.add(t); + auto it = find_annesso(catdoc, n); + if (it != _rows_annessi.end()) + { + TToken_string t; + t.add(it->second.catdocpadre); + t.add(it->second.catdoc); + t.add(it->second.descr); + t.add(it->second.opcee); + t.add(it->second.obblig); + _result_set.add(t); + } } } return next(); @@ -1332,10 +1463,12 @@ TRecord_categorie::TRecord_categorie(type table): _table(table) _categorie.head.add("CAUSSOS"); _categorie.head.add("CAUSCONT"); _categorie.head.add("TIPODOC"); - _categorie.head.add("CLASSANN"); _annessi.head.add("CATDOC"); _annessi.head.add("NAME"); + _annessi.head.add("DESCR"); + _annessi.head.add("TIPOANN"); + _annessi.head.add("OBBLIG"); } diff --git a/src/f9/f9lib01.h b/src/f9/f9lib01.h index 28896b52c..efb940977 100644 --- a/src/f9/f9lib01.h +++ b/src/f9/f9lib01.h @@ -133,40 +133,53 @@ class TCategorie_doc public: struct classe_doc { - TString catdoc; - TString descr; - TString class_sost; // { FTA | FTV } - classe documentale sostitutiva - TString caus_sost; // causale per sostitutiva(TD01…) - TString causcont; - TString tipocaus; - TString tipomov; - TString opcee; + TString catdoc; // Codice cat. documentale. + TString descr; // Descrizione documento. + TString class_sost; // { FTA | FTV } - classe documentale sostitutiva. + TString caus_sost; // causale per sostitutiva (TD01...). + TString causcont; // Codice causale. + TString tipocaus; // Usato come tipo documento (FA, FV, CR, ...). + }; + struct annesso + { + TString catdocpadre; + TString catdoc; // Codice cat. documentale. + TString descr; // Descrizione documento. + TString opcee; // { RV | DC } - Usato come classificazione annesso (Reverse, ann. cartaceo). + bool obblig; // Definisce se deve essere per forza presente l'annesso. }; private: std::vector> _rows; - std::vector> _annessi; std::vector>> _sheets_annessi; std::shared_ptr _sheet_catdocs; std::shared_ptr> _name_catdocs; + int _mode_sheet; - std::vector>::iterator find(const TString& class_sost, const TString& caus_sost, - const TString& op_cee); - std::shared_ptr find_causcont(const TString& caus); - std::shared_ptr find_tipodoc(const TString& tipodoc); - - std::vector>::iterator find_annesso(const TString& catdoc, const char* catdoc_ann); - std::vector>>::iterator find_sheet_annessi(const TString& catdoc); + std::shared_ptr find_causcont(const TString& caus); // OK + std::shared_ptr find_tipodoc(const TString& tipodoc); // OK + std::vector>>::iterator find_sheet_annessi(const TString& catdoc); // OK void load_all(); + void save_ann(); + void save_cat(); static const char* traduci_caus_sost(const TString& caus); static const char* traduci_class_ann(const TString& class_ann); static const char* traduci_class_sost(const TString& class_sost); + +protected: + std::map _rows_annessi; + + std::map::iterator find_annesso(const TString& catdoc_padre, const char* catdoc_ann); + public: - //std::shared_ptr causcont2cat(const char* caus, bool force_cartacea); - void add_annesso(const TString& catdoc, const char* catdoc_ann); + void add_annesso(const TString& catdoc_padre, const TString& catdoc_ann, const TString& descr, + const TString& class_ann, bool obblig = false); + void add_categoria(const TString& catdoc, const TString& descr, const TString& class_sost, + const TString& caus_sost, const TString& causcont, const TString& tipocaus); void del_annesso(const TString& catdoc, const char* catdoc_ann); + bool get_ann(const TString& catann, annesso& annesso); TString_array get_array_ann(const TString& catdoc); TString_array get_array_rows(bool traduci = false); std::shared_ptr get_classe_doc(const TString& catdoc); @@ -175,8 +188,12 @@ public: std::shared_ptr get_sheet_ann(const TString& catdoc); std::shared_ptr mov2cat(int numreg); void reload(); + void remove_all(); + static void remove_all_ann(); + static void remove_all_cat(); + void set_mode_sheet(const int mode) { _mode_sheet = mode; } - TCategorie_doc() { load_all(); } + TCategorie_doc(); }; class TRecord_categorie : public TCategorie_doc @@ -223,7 +240,8 @@ struct movimento_t no_fv, // Movimento di vendita non e' una fattura. no_cartaceo, // Movimento cartaceo che non ha bisogno di essere estratto. pura_iva, // Movimento di sola IVA (integrazione Rev. Charge) - no_filecart // Il documento cartaceo non ha associato un file, o questo non e' stato trovato. + no_filecart, // Il documento cartaceo non ha associato un file, o questo non e' stato trovato. + annesso_nexist // Un annesso obbligatorio e' mancante. }; bool err; @@ -239,7 +257,7 @@ struct movimento_t TString reg_protiva; TString descr; state_fppro state{ null_state }; - TString descr_err; // Messaggio di errore visibile dal controllo estrazion + TString descr_err; // Messaggio di errore visibile dal controllo estrazione. bool estratto{ true }; err_mov descr_estr{ no_err }; // Messaggio di informazioni visibile dal 'Apri Estr.' std::shared_ptr catdoc; @@ -254,7 +272,7 @@ struct movimento_t case no_err: return "OK"; case escluso: - return "Il movimemnto e' segnato come escluso. Saltato."; + return "Il movimento e' segnato come escluso. Saltato."; case no_catdoc: return "Nessuna categoria documentale riconosciuta per questo movimento, o non e' una fattura."; case no_doc: @@ -269,6 +287,8 @@ struct movimento_t return "Movimento cartaceo che non ha bisogno di essere estratto"; case pura_iva: return "Movimento di sola IVA. (Le integrazioni Rev. Charge sono gestite come annessi alle vendite)"; + case annesso_nexist: + return "Un annesso obbligatorio e' mancante."; default: return ""; } } @@ -323,6 +343,8 @@ class TEstrazione : public TObject bool update_drd_stato_estr() const; static const char* categoria_doc(); static const char* caus_sos(const TLocalisamfile& mov, TipoIVA iva); + static void check_annessi(movimento_t& mov_i, const TString& numreg); + static bool check_annessi_oblig(const TString& catdoc, const TString& numreg, TToken_string& ann_nexist); static bool check_documento_vendita(const TLocalisamfile& mov, _Out_ bool& exist_doc); /** CHECK RIFERIMENTO FPPRO * Per le fatture di acquisto controllo se ho il riferimento nell'FPPRO. @@ -402,7 +424,7 @@ public: void set_addrcart(const TString& addrcart) { _head.addr_cart = addrcart; } TEstrazione(const TString& ambiente, bool flag_prov, char tipodoc, const TString& descr, const TString& addrcart, - bool escluso, const TDate* dal = nullptr, const TDate* al = nullptr, bool has_vendext = false, bool has_cartacei = false); + bool escluso, const TDate* dal = nullptr, const TDate* al = nullptr, bool has_checkvend = false, bool has_cartacei = false); }; @@ -450,6 +472,11 @@ public: bool mov2listann(const TString& numreg, _Out_ TString_array& list_annessi); bool mov2listann_vect(const TString& numreg, _Out_ vector& list_annessi); + static TString get_full_path_file_cartaceo(const TString& filename) + { + return TString() << TFilename(F9CONF.get_addr_cart()).slash_terminate() << filename; + } + TF9_doccart() : _tdocs(LF_F9DOCS), _tannessi(LF_F9ANNESSI) { } }; diff --git a/src/f9/f9lib02.cpp b/src/f9/f9lib02.cpp index 6256c2957..4268c2878 100644 --- a/src/f9/f9lib02.cpp +++ b/src/f9/f9lib02.cpp @@ -198,7 +198,6 @@ void TIva_insert_prepared_stat::write() count = ++count % 3; } _query << "\n)\nVALUES (\n" << vals_appo << "\n)"; - bool simo = true; } void TIva_insert_prepared_stat::add(const char* field, const TString& value) From 0312bf732c9d49769d53c64da60fccd29271d2f0 Mon Sep 17 00:00:00 2001 From: Sirio Builder Date: Thu, 28 May 2020 17:29:25 +0200 Subject: [PATCH 2/2] 2Patch level : 12.0 966 Files correlati : ba Commento : patch --- cd/test/ba0966.txt | 6 ++++++ cd/test/ba0966a.ini | 34 ++++++++++++++++++++++++++++++++++ cd/test/ba0966a1.zip | Bin 0 -> 961 bytes 3 files changed, 40 insertions(+) create mode 100644 cd/test/ba0966.txt create mode 100644 cd/test/ba0966a.ini create mode 100644 cd/test/ba0966a1.zip diff --git a/cd/test/ba0966.txt b/cd/test/ba0966.txt new file mode 100644 index 000000000..413febde9 --- /dev/null +++ b/cd/test/ba0966.txt @@ -0,0 +1,6 @@ +recdesc\f183.dir +recdesc\f183.trr +recdesc\f181.dir +recdesc\f181.trr + +Aggiunta tabella per annessi f9 diff --git a/cd/test/ba0966a.ini b/cd/test/ba0966a.ini new file mode 100644 index 000000000..0000e2dad --- /dev/null +++ b/cd/test/ba0966a.ini @@ -0,0 +1,34 @@ +[Main] +Demo=0 + +[ba1] +File(537) = recdesc\f181.trr|X +File(538) = recdesc\f181.dir|X +File(539) = recdesc\f183.trr|X +File(540) = recdesc\f183.dir|X +Patch = 0966 +Versione = 21511200 + +[ba99] +Kill(0) = batbsce.txt|x +Kill(1) = wxmsw240.dll|x +Kill(2) = bastcms.rep|x +Kill(3) = bastcms.msk|x +Kill(4) = bastfsc.msk|x +Kill(5) = bastfsc.rep|x +Kill(6) = bastuue.msk|x +Kill(7) = bastuue.rep|x + +[ba] +Data = 28-05-2020 +Descrizione = Base +Dischi = 1 +Moduli = sy +OEM = +Patch = 966 +PostProcess = bainst -0 BA +PreProcess = +Prezzo(1) = +Prezzo(2) = +Versione = 21511200 + diff --git a/cd/test/ba0966a1.zip b/cd/test/ba0966a1.zip new file mode 100644 index 0000000000000000000000000000000000000000..2b1544bd93e8e6072269fd896a1c1fefeac43efd GIT binary patch literal 961 zcmWIWW@Zs#U|`^2sOsJmklK}?vjWI_48#IJT$Gxel3JXspJr%bs8>={-E1Jz z`rYB*VcFvMcW&T*dFA}l$rrqw*UAK*J@HKImE`vMmyY?$Io($NvVYcP2YH+H zlm88M_UU|iCVk(snmGXKD+aKymRz5%=K}N%2xIsvC9??VIbJRUUM`iC{Nyyt8si$X z8bf2F8iN|g;^O?|%*3k9{Jc~Jprk@_Vo^zAa%!f6zCvz(nL%tlB! zZdJ(Ry92b%j}gO0V^BEOPH@a-G8AZi-}tB9a@&_p(HE_oVnfRwEoUhZ?Cr|F&r0odBVBNqUZyQ1O23|%+;6#LrTVK2zK5)OFLZU=fA2a*&1=z1x89m0gzjqB^as4`V6S=>$1WcZ&m>Cw+`%a;GH`tiBbUvnQLYDlcD@9QuIhJ+<% z*cku3%y{ zKRs)fRGc0|fHxzP3