#include #include #include #include #include #include #include #include #include #include "../cg/cgsaldac.h" #include "../ef/ef0101.h" #include "../ve/velib.h" #include "halib.h" #include "ha0.h" #include "ha0500a.h" /////////////////////////////////////////////////////////// // TAutomask /////////////////////////////////////////////////////////// class THardy_elab_docs_mask : public TAutomask { protected: virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); public: THardy_elab_docs_mask() : TAutomask ("ha0500a") {} }; bool THardy_elab_docs_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) { switch (o.dlg()) { case F_DADATA: if ((e == fe_close || e == fe_modify) && !o.empty()) { //se la data iniziale è piena -> l'anno deve essere lo stesso nelle 2 date; //se invece è vuota -> la data iniziale viene presa come la data iniziale dell'esercizio della data finale... //..ma questo qui non serve e viene rinviato alla query principale del recordset TDate da_data = o.get(); const TDate a_data = get_date(F_ADATA); if (da_data.ok()) { bool changed = false; if (da_data.day() != 1) { da_data.set_day(1); changed = true; } const int tc = get_int(F_TIPOCONTR); if (tc != 8) // non è nolo { const int m = da_data.month(); const int r = m % 3; if (r != 1) { da_data.set_month(m - (r == 0 ? 2 : (r-1))); changed = true; } } if (changed) o.set(da_data.string()); } } break; case F_ADATA: if ((e == fe_close || e == fe_modify) && !o.empty()) { const TDate al = o.get(); const TDate dal = get(F_DADATA); if (dal.ok() && dal.year() != al.year()) return error_box(TR("Le date devono appartenere allo stesso anno!")); } break; //in caso di elaborazione definitiva è obbligatorio.. //..eliminare tutte le NAC provvisorie generate in precedenza case F_DEFINITIVO: if (e == fe_modify) { if (o.get().full()) { set(F_KILLPROVV, "X"); disable(F_KILLPROVV); } else { reset(F_KILLPROVV); enable(F_KILLPROVV); } } break; default: break; } return true; } /////////////////////////////////////// // TSkeleton_application /////////////////////////////////////// class THardy_elab_docs : public TSkeleton_application { protected: //metodi alto livello void elabora(const TMask& mask); int kill_provv_nac(const TMask& mask); long genera_recordset(const TMask& mask, TISAM_recordset& recset); void elabora_documenti(const TMask& mask, TISAM_recordset& recset, TLog_report& log); //metodi medio livello bool aggiorna_contratto(const TRiga_documento& rdoc, TContratto_premi& contratto, TLog_report& log); bool deve_generare_nac(const TContratto_premi& contratto, const TDate& data) const; word elabora_contratti(TDocumento& curr_doc, TArray& contratti_cliente, const TDate& data_fine, TLog_report& log); bool genera_nac(const TMask& mask, TArray& contratti_cliente, TAssoc_array& nac_nolo, TArray& documenti_cliente, TLog_report& log); bool genera_fat(const TMask& mask, TContratto_premi& contratto, TToken_string& nakey, TLog_report& log); bool genera_eff(const TDocumento& fat, const real& tot_nac, TLog_report& log); //metodi basso livello word find_contratti_cliente(const long codcfatt, const TMask& mask, int flags, char freq, TArray& contratti_cliente); void check_date(const TDate& datafine, TDate& dataini); int find_numerazioni(const TString& tipo_to_elab, TString_array& num_doc); int month_diff(const TDate& inizio, const TDate& fine) const; public: virtual void main_loop(); virtual bool create(); }; //metodo per ricavare la data iniziale di elaborazione qualora l'utonto non la metta e l'esercizio da usare void THardy_elab_docs::check_date(const TDate& datafine, TDate& dataini) { TEsercizi_contabili esc; TDate datafine_tmp = datafine; const int esercizio = esc.date2esc(datafine); esc.code2range(esercizio, dataini, datafine_tmp); } int THardy_elab_docs::find_numerazioni(const TString& tipo_to_elab, TString_array& num_doc) { TISAM_recordset num_recset("USE %NUM"); const TRectype& rec = num_recset.cursor()->curr(); for (bool ok = num_recset.move_first(); ok; ok = num_recset.move_next()) //giro sui vari tipi numerazione { const TString4 codtab = rec.get("CODTAB"); const TCodice_numerazione numerazione(rec); for (int t = numerazione.ntipi_doc() - 1; t >= 0; t--) { const TString& tipo_doc = numerazione.tipo_doc(t); if (tipo_doc == tipo_to_elab) { if (num_doc.find(codtab) < 0) // Evito aggiunta di doppioni num_doc.add(codtab); break; } } //for (int t = codnum.. } //for (bool ok = num_recset... return num_doc.items(); } //metodo per accoppare tutte le NAC provvisorie generate in precedenza int THardy_elab_docs::kill_provv_nac(const TMask& mask) { const TDate& adata = mask.get_date(F_ADATA); const int anno = adata.year(); TConfig config(CONFIG_DITTA, "ha"); TToken_string numerazioni; numerazioni.add(config.get("NaAntNum")); numerazioni.add(config.get("NaNoloNum")); numerazioni.add(config.get("NaPostNum")); numerazioni.add(config.get("NaRifaNum")); int nac_killed = 0; TRelation rel_doc(LF_DOC); TRectype& rec = rel_doc.curr(); FOR_EACH_TOKEN(numerazioni, codnum) if (*codnum > ' ') { rec.zero(); rec.put(DOC_PROVV, 'P'); rec.put(DOC_ANNO, anno); rec.put(DOC_CODNUM, codnum); TCursor cur_doc (&rel_doc, "", 1, &rec, &rec); const TRecnotype items = cur_doc.items(); cur_doc.freeze(); TProgind progind(items, TR("Eliminazione NAC provvisorie in corso...")); for (cur_doc = 0; cur_doc.pos() < items; ++cur_doc) { if (!progind.addstatus(1)) break; TDocumento doc(rec); if (doc.remove() == NOERR) nac_killed++; } } return nac_killed; } //metodo che filtra tutti i documenti in base ai parametri della maschera long THardy_elab_docs::genera_recordset(const TMask& mask, TISAM_recordset& recset) { const int tc = mask.get_int(F_TIPOCONTR); //parametri di elaborazione //per prima cosa controlla se il cliente è stato specificato; questo influisce sulla scelta della chiave di ricerca sul file int key = 3; const long codcf = mask.get_long(F_CODCF); if (codcf > 0) key = 2; //e costruiamola 'sta query! //usa i documenti con chiave variabile (vedi sopra) TString query; query << "USE DOC KEY " << key; //lo stato dipende da quanto sta scritto sulla elaborazione differita (stato iniziale dei docs da considerare) //viene messo CODNUM nella SELECT perchè, essendoci un range di date nelle chiavi, la numerazione verrebbe ignorata! (provato!) query << "\nSELECT (STATO>=#STATOINI)"; if (tc != 8) // Per i contratti NON di nolo ... query << "&&(STATO<#STATOFIN)"; // ... scarta i documenti definitivi //in base al tipo documento che si deve elaborare (settato in configurazione), ci possono essere più numerazioni da considerare! TConfig config(CONFIG_DITTA, "ha"); const TString4 tipo_doc_to_elab1 = config.get("TipoFatt"); const TString4 tipo_doc_to_elab2 = config.get("TipoFatt2"); // Inutile testare tutte le numerazioni, basta il tipo documento query << "&&((" << DOC_TIPODOC << "=='" << tipo_doc_to_elab1 << "')||(" << DOC_TIPODOC << "=='" << tipo_doc_to_elab2 << "'))"; if (codcf <= 0) query << "\nBY " << DOC_CODCF << ' ' << DOC_NDOC; //ordinamento codcf-ndoc //from-to dipendente da chiave switch (key) { case 2: //chiave per tipocf-codcf-provv-anno-datadoc-codnum query << "\nFROM TIPOCF=C CODCF=#CODCF PROVV=D ANNO=#ANNO DATADOC=#DADATA" << "\nTO TIPOCF=C CODCF=#CODCF PROVV=D ANNO=#ANNO DATADOC=#ADATA"; break; case 3: //chiave per datadoc-provv-anno-codnum query << "\nFROM DATADOC=#DADATA PROVV=D ANNO=#ANNO" << "\nTO DATADOC=#ADATA PROVV=D ANNO=#ANNO"; break; default: break; } //setta la nuova query al recordset (inizialmente nato dequeryzzato) recset.set(query); //settaggio delle variabili const TDate adata = mask.get_date(F_ADATA); TDate dadata = mask.get_date(F_DADATA); //se la data iniziale è vuota deve coincidere con l'inizio dell'esercizio della data finale (obbligatoria!) if (!dadata.ok()) check_date(adata, dadata); //lo stato dei documenti da considerare va a raccatarlo nel config const TString& stato_ini = config.get("StatoIniFatt"); const TString& stato_fin = config.get("StatoFinFatt"); recset.set_var("#STATOINI", stato_ini); recset.set_var("#STATOFIN", stato_fin); //se c'è l'agente specificato... const TString& agente = mask.get(F_CODAGE); if (agente.full()) recset.set_var("#CODAG", agente); if (codcf > 0) recset.set_var("#CODCF", codcf); recset.set_var("#ANNO", long(adata.year())); recset.set_var("#DADATA", dadata); recset.set_var("#ADATA", adata); return recset.items(); } //metodo che riempie un array con tutti i contratti del cliente passatogli (in base alla tipologia di contratti da elaborare) // flags: 0x7 = tutti tranne nolo; 0x8 = nolo word THardy_elab_docs::find_contratti_cliente(const long codcfatt, const TMask& mask, int flags, char selected_freq, TArray& contratti_cliente) { word freq = 0; contratti_cliente.destroy(); const TDate data_fine_elab = mask.get_date(F_ADATA); TDate data_ini_elab = mask.get_date(F_DADATA); if (!data_ini_elab.ok()) check_date(data_fine_elab, data_ini_elab); // settaggio delle variabili // il codice numerazione lo trova nella configurazione Hardy, // lo deve scegliere in base alla tipologia di contratti che sta esaminando! for (int f = 0; f < 4; f++) if (flags & (1 << f)) { TString4 codnum, tipodoc; switch (f) { case 0: codnum = ini_get_string(CONFIG_DITTA, "ha", "CoAntNum"); tipodoc = ini_get_string(CONFIG_DITTA, "ha", "CoAntTip"); break; case 1: codnum = ini_get_string(CONFIG_DITTA, "ha", "CoRifaNum"); tipodoc = ini_get_string(CONFIG_DITTA, "ha", "CoRifaTip"); break; case 2: codnum = ini_get_string(CONFIG_DITTA, "ha", "CoPostNum"); tipodoc = ini_get_string(CONFIG_DITTA, "ha", "CoPostTip"); break; case 3: codnum = ini_get_string(CONFIG_DITTA, "ha", "CoNoloNum"); tipodoc = ini_get_string(CONFIG_DITTA, "ha", "CoNoloTip"); break; default: break; } const TString& agente = mask.get(F_CODAGE); //deve cercare tutti i contratti del cliente e metterli nell'array TString query; query << "USE DOC KEY 5"; query << "\nSELECT (TIPODOC=#TIPODOC)&&(STATO!=9)"; if (codcfatt > 0) query <<"&&(STR(DOC2=#CODCF))"; if (agente.full()) query << "&&(CODAG=#CODAG)"; query << "\nFROM PROVV=D CODNUM=" << codnum; query << "\nTO PROVV=D CODNUM=" << codnum; TISAM_recordset recset(query); recset.set_var("#TIPODOC", tipodoc); if (codcfatt > 0) recset.set_var("#CODCF", codcfatt); if (agente.full()) recset.set_var("#CODAG", agente); const long n_contratti = recset.items(); //questo serve solo al sagace programmatore //aggiunge i contratti all'array: solo quelli in auge nel periodo di calcolo selezionato sulla maschera! for (bool ok = recset.move_first(); ok; ok = recset.move_next()) { // Ignora contratti conm frequenza indesiderata if ( (f < 3) && selected_freq >= 'A') { char fr = recset.get(DOC_CATVEN).as_string()[0]; if (fr < 'A') fr = 'A'; if (fr != selected_freq) continue; } //contratti anticipo 'A': datainizio esiste sempre, datafine non esiste (va ad esaurimento) //contratti nolo 'N': datainizio esiste sempre, datafine esiste sempre //contratti posticipo 'P': datainizio esiste sempre, datafine può non esistere //contratti rifatturazione 'R': come contratti anticipo //controlla validità del contratto con le date scelte per l'elaborazione dei documenti const TDate data_ini_contratto = recset.get(DOC_DATACOMP).as_date(); //quindi la datainizio vale per tutti allo stesso modo (è obbligatoria nei contratti) //se l'elaborazione finisce prima che cominci il contratto -> il contratto non serve a nulla if (data_ini_contratto > data_fine_elab) continue; //la data fine vale invece solo per i contratti 'P' e potrebbe non esserci (contratti senza scadenza) TDate data_fine_contratto = recset.get(DOC_DATAFCOMP).as_date(); if (f == 3) // if (tipo_contratto == "N") { const TDate data_recesso = recset.get(DOC_DATASCIMP).as_date(); if (data_recesso.ok()) data_fine_contratto = data_recesso; else { const int rate = recset.get(DOC_NCOLLI).as_int(); const int paga = recset.get(DOC_NUMANT).as_int(); if (paga >= rate) // Fine pagamento rate continue; } } //se la data fine contratto non è valida (ma è presente!) non dobbiamo fare nulla, perchè il contratto non va elaborato if (data_fine_contratto.ok() && data_fine_contratto < data_ini_elab) continue; //ci tocca istanziarci un contratto_premi TContratto_premi* curr_contratto = new TContratto_premi(recset.cursor()->curr()); //azzeratore del campo con il totale reso per elaborazione, nel caso di contratti Anticipo/Rifatturazione, riga spese const char tipo_contr = curr_contratto->tipo_contratto(); FOR_EACH_PHYSICAL_RDOC(*curr_contratto, r, rdoc) { const TString& tr = rdoc->get(RDOC_TIPORIGA); if (tr == HARDY_TIPORIGA_SOMMA && (tipo_contr == 'A' || tipo_contr == 'R')) { rdoc->zero(RCA_2_RESO_CORRENTE); const real prezzo = rdoc->prezzo(false, false); const real resost = rdoc->get(RCA_2_RESO_STORICO); if (resost >= prezzo) // Contratto esaurito da ignorare { delete curr_contratto; curr_contratto = NULL; } break; } else if (tr == HARDY_TIPORIGA_MERCE) rdoc->zero(RDOC_QTA); } if (curr_contratto != NULL) // Ignora contratti chiusi { contratti_cliente.add(curr_contratto); switch (curr_contratto->frequenza()) { case 'A': freq |= 0x8; break; case 'S': freq |= 0x4; break; case 'T': freq |= 0x2; break; default : freq |= 0x1; break; } } } } return freq; } int THardy_elab_docs::month_diff(const TDate& inizio, const TDate& fine) const { const int i = inizio.year()*12 + inizio.month(); const int f = fine.year()*12 + fine.month(); return f-i; } bool THardy_elab_docs::aggiorna_contratto(const TRiga_documento& rdoc, TContratto_premi& contratto, TLog_report& log) { bool elaborato = false; const bool definitivo = rdoc.get_char(RDOC_PROVV) == 'D'; const char tipo_contratto = contratto.tipo_contratto(); //parametri della riga fattura TString80 rdoc_codart = rdoc.get(RDOC_CODART); rdoc_codart.trim(); const real rdoc_qta = rdoc.get_real(RDOC_QTA); const TString4 rdoc_umqta = rdoc.get(RDOC_UMQTA); FOR_EACH_PHYSICAL_RDOC(contratto, rm, rigamerce) { const TString& rigamerce_codart = rigamerce->get(RDOC_CODART); //se trova il codart in una delle righe di contratto... if (rdoc_codart == rigamerce_codart) { const real rigamerce_premio = rigamerce->get_real(RC_1_PREMIO); //se il premio non è nullo procede all'aggiornamento del restituito (solo contratti A/R e righe spese) e del bonus (tutti i contratti e righe merce) if (rigamerce_premio != ZERO) { //normalizzazione della qta const TString& umqta_tot = rigamerce->get(RDOC_UMQTA); //prende la UM dal contratto che farà da riferimento! TArticolo articolo(rdoc_codart); const real normalized_rdoc_qta = articolo.convert_to_um(rdoc_qta, umqta_tot, rdoc_umqta, true); //aggiornamento delle righe di tipo spesa (verig02) per aggiornare le somme restituite nel caso di contratti di anticipo/rifatturazione if (tipo_contratto == 'A' || tipo_contratto == 'R') { FOR_EACH_PHYSICAL_RDOC(contratto, ra, rigacontratto) { //cerca una riga anticipo da evadere sul contratto per aggiornare la somma restituita sull'anticipo if (rigacontratto->is_spese()) { if (definitivo) // Non scrivo aaggiornamenti provvisori { //la somma restituita deve essere incrementata per la qta di merce (caffè) che c'è sulla riga della fattura in esame... //..moltiplicata per il premio che c'è nella riga di tipo merce del contratto in esame const real reso = normalized_rdoc_qta * rigamerce_premio; rigacontratto->add(RCA_2_RESO_CORRENTE, reso); } elaborato = true; break; } } //FOR_EACH_PHYSICAL.. fine casino sulla riga di tipo spese } //if(tipo_contratto == "A"... //questo va bene invece per ogni riga merce (verigh01), sia per contratti di tipo A/R che di tipo P if (rigamerce->is_merce()) { const real bonus = normalized_rdoc_qta * rigamerce_premio; if (definitivo) // Non scrivo aaggiornamenti provvisori { rigamerce->add(RC_1_BONUS, bonus); rigamerce->add(RDOC_QTA, normalized_rdoc_qta); //riscrive sul campo di appoggio del contratto } elaborato = true; TString msg; msg.format("Doc. %d/%s %ld - %s: %s %s -> Bonus %s Contr. %d/%s %ld", rdoc.get_int(RDOC_ANNO), (const char*)rdoc.get(RDOC_CODNUM), rdoc.get_long(RDOC_NDOC), (const char*)rdoc_codart, normalized_rdoc_qta.stringa(4, 0), (const char*)rdoc_umqta, bonus.stringa(7,2), contratto.get_int(DOC_ANNO), (const char*)contratto.get(DOC_CODNUM), contratto.get_long(DOC_NDOC)); log.log(0, msg); } } //if(rigamerce_premio != ZERO... } //if(rdoc_codart... } //FOR_EACH_PHYSICAL.. return elaborato; } bool THardy_elab_docs::deve_generare_nac(const TContratto_premi& contratto, const TDate& data) const { const int mese = data.month(); bool yes = false; switch (contratto.frequenza()) { case 'A': yes = mese == 12; break; // Gli annuali valgono solo a dicembre case 'M': yes = true; break; // I mensili (nolo) valgono sempre case 'S': yes = mese == 6 || mese == 12; // I semestrali valgono solo a giugno e dicembre case 'T': yes = data.month() % 3 == 0; break; // I trimestrali valgono solo a marzo, giugno, settembre e dicembre default: break; } return yes; } //aggiorna, in base al documento in esame curr_doc, le somme restituite nelle righe di tipo verigh02 nei contratti validi word THardy_elab_docs::elabora_contratti(TDocumento& curr_doc, TArray& contratti_cliente, const TDate& data_fine, TLog_report& log) { const TDate datadoc = curr_doc.get(DOC_DATADOC); word elaborato = 0; TString4 olddoc2 = curr_doc.get(DOC_DOC2); olddoc2.rpad(4); // Quattro spazi o X in corrispondenza dell'elaborazione TString4 newdoc2 = olddoc2; FOR_EACH_PHYSICAL_RDOC(curr_doc, r, rdoc) if (rdoc->is_merce()) //giro su tutte le righe merce delle fatture { //controlla se il codart della riga esiste in uno o più contratti validi for (int i = 0; i < contratti_cliente.items(); i++) { TContratto_premi& contratto = (TContratto_premi&)contratti_cliente[i]; if (!contratto.data_valida(datadoc)) continue; // Calcola l'inizio di validità dei documenti in base alla frequenza (14-01-2013) TDate data_inizio(1, 1, data_fine.year()); const int mese = data_fine.month(); int pdef = 0; switch (contratto.frequenza()) { case 'M': pdef = 0; data_inizio.set_month(mese); break; // Solo contratti NOLO case 'S': pdef = 2; data_inizio.set_month(mese > 6 ? 7 : 1); break; case 'T': pdef = 1; data_inizio.set_month(max(1, mese-2)); break; default : pdef = 3; break; } // Elabora solo i documenti che ricadano nell'intervallo valido if (datadoc >= data_inizio && datadoc <= data_fine && olddoc2[pdef] <= ' ') { if (aggiorna_contratto(*rdoc, contratto, log)) { switch (contratto.frequenza()) { case 'A': elaborato |= 0x8; break; case 'S': elaborato |= 0x4; break; case 'T': elaborato |= 0x2; break; default : elaborato |= 0x1; break; // Solo contratti NOLO } if (deve_generare_nac(contratto, data_fine)) newdoc2.overwrite("X", pdef, 1); } } } //for(int i.. } //FOR_EACH... if (newdoc2 != olddoc2) curr_doc.put(DOC_DOC2, newdoc2); return elaborato; } bool THardy_elab_docs::genera_nac(const TMask& mask, TArray& contratti_cliente, TAssoc_array& fat_nolo, TArray& documenti_cliente, TLog_report& log) { bool una_nac_definitiva = false; if (contratti_cliente.empty()) return una_nac_definitiva; //si informa se l'elaborazione è definitiva o meno const bool definitivo = mask.get_bool(F_DEFINITIVO); const TDate data_fine = mask.get(F_ADATA); //giro su tutti i contratti del cliente che stanno nell'array (ogni contratto genera una NAC) FOR_EACH_ARRAY_ITEM(contratti_cliente, r, riga) { TContratto_premi& contratto = *(TContratto_premi*)riga; const long ndoc = contratto.numero(); // il numdoc del contratto serve nelle segnalazioni const long codcf = contratto.codcf(); // il codice cliente ci serve nella generazione della NAC... const char tipo_contratto = contratto.tipo_contratto(); // ...e pure il tipo di contratto in esame! TString80 tip = ""; switch (tipo_contratto) { case 'A': tip = TR("Anticipo"); break; case 'N': tip = TR("Nolo"); break; case 'P': tip = TR("Posticipo"); break; case 'R': tip = TR("Rifatturzaione"); break; default : break; } const bool nac_ok = deve_generare_nac(contratto, data_fine); TString80 freq = ""; switch (contratto.frequenza()) { case 'A': freq = TR("annuale"); break; case 'M': freq << TR("rata ") << (contratto.get_int(DOC_NUMANT)+1); break; case 'S': freq << (data_fine.month() < 7 ? TR("I sem.") : TR("II sem.")); break; case 'T': freq << itor((data_fine.month()/3)) << TR(" trim."); break; default : break; } //segnaliamo l'elaborazione del contratto sul log TString log_msg; log_msg.format(FR("Cliente:%ld Contratto:%ld %s %s"), codcf, ndoc, (const char*)tip, (const char*)freq); log_msg << "(NAC "; if (nac_ok) log_msg << "def."; else log_msg << "provv."; log_msg << ')'; // generazione del documento NAC dal contratto // ------------------------------------------- // TESTATA //alcuni parametri delle righe vanno presi dalla configurazione TString4 nac_codnum, nac_tipo; TString8 cod_riga; switch (tipo_contratto) { case 'A': nac_codnum = ini_get_string(CONFIG_DITTA, "ha", "NaAntNum"); nac_tipo = ini_get_string(CONFIG_DITTA, "ha", "NaAntTip"); cod_riga = ini_get_string(CONFIG_DITTA, "ha", "NaAntSpe"); break; case 'N': nac_codnum = ini_get_string(CONFIG_DITTA, "ha", "NaNoloNum"); nac_tipo = ini_get_string(CONFIG_DITTA, "ha", "NaNoloTip"); cod_riga = ini_get_string(CONFIG_DITTA, "ha", "NaNoloSpe"); break; case 'R': nac_codnum = ini_get_string(CONFIG_DITTA, "ha", "NaRifaNum"); nac_tipo = ini_get_string(CONFIG_DITTA, "ha", "NaRifaTip"); cod_riga = ini_get_string(CONFIG_DITTA, "ha", "NaRifaSpe"); break; default: nac_codnum = ini_get_string(CONFIG_DITTA, "ha", "NaPostNum"); nac_tipo = ini_get_string(CONFIG_DITTA, "ha", "NaPostTip"); cod_riga = ini_get_string(CONFIG_DITTA, "ha", "NaPostSpe"); break; } const TString8 codage = contratto.get(DOC_CODAG); if (codage.full()) // Controlla se esiste una spesa su misura per l'agente { TString8 codspesa = cod_riga; codspesa << codage.right(3); const TRectype& rec_spp = cache().get("SPP", codspesa); if (!rec_spp.empty()) cod_riga = codspesa; } const TDate datadoc = mask.get(tipo_contratto == 'N' ? F_DATANOLO : F_DATAELAB); const int anno = datadoc.year(); //solo in caso di elaborazione definitiva si scrivono NAC di tipo D; //sennò di tipo P, che sono uccidibili all'inizio di ogni nuova elaborazione const char provv = definitivo && nac_ok ? 'D' : 'P'; TDocumento nac(provv, anno, nac_codnum, 0); //num_doc = 0 perchè viene aggiornato in fase di registrazione nac.set_tipo(nac_tipo); nac.put(DOC_STATO, 1); nac.put(DOC_DATADOC, datadoc); nac.put(DOC_TIPOCF, 'C'); nac.put(DOC_CODCF, codcf); nac.put(DOC_CODAG, codage); nac.put(DOC_CODPAG, contratto.get(DOC_CODPAG)); nac.put(DOC_DOC1, contratto.get(DOC_DOC1)); TToken_string cnkey; // Chiave contratto cnkey.add(contratto.get(DOC_PROVV)); cnkey.add(contratto.get(DOC_ANNO)); cnkey.add(contratto.get(DOC_CODNUM)); cnkey.add(contratto.get(DOC_NDOC)); TToken_string* fatkey = (TToken_string*)fat_nolo.objptr(cnkey); if (fatkey != NULL) { // Skip fatkey->get(0) = PROVV nac.put(DOC_ANNORIF, fatkey->get(1)); const TString4 codnum = fatkey->get(); nac.put(DOC_CODNUMRIF, codnum); const TCodice_numerazione& cn = cached_numerazione(codnum); TString80 numdoc; numdoc << cn.prefisso() << fatkey->get() << cn.postfisso(); nac.put(DOC_NUMDOCRIF, numdoc); nac.put(DOC_DATADOCRIF, fatkey->get()); } else nac.put(DOC_DATADOCRIF, contratto.get(DOC_DATADOCRIF)); // RIGHE //ogni riga di tipo merce (verigh01) del contratto origina una riga della NAC //noto il codice di riga spesa (che farà le veci del codart), troviamo il tipo riga dalla tabella SPP e tutte le features che servono const TRectype& rec_spp = cache().get("SPP", cod_riga); const TString4 tipo_riga_spp = rec_spp.get("S8"); const TString80 descr_riga_spp = rec_spp.get("S0"); const TString4 codiva = rec_spp.get("S3"); TDate adata = mask.get_date(F_ADATA); TDate dadata = mask.get_date(F_DADATA); if (!dadata.ok()) check_date(adata, dadata); TDate dmin(1, 1, adata.year()); switch (contratto.frequenza()) { case 'M': dmin.set_month(adata.month()); break; // Solo nolo case 'S': dmin.set_month(adata.month() > 6 ? 7 : 1); break; case 'T': dmin.set_month(max(1, adata.month()-2)); break; default: break; } if (dmin > dadata) dadata = dmin; if (!contratto.data_valida(dadata)) dadata = contratto.data_inizio(); if (!contratto.data_valida(adata)) adata = contratto.data_fine(); TString msg_date; msg_date << " " << dadata << " -- " << adata; //giro sulle righe del contratto, che originano le righe NAC for (int i = 1; i <= contratto.rows(); i++) { const TRiga_documento& riga_contratto = contratto[i]; //le righe di un documento partono da 1! (standard del mercoledì) const TString& tiporiga = riga_contratto.get(RDOC_TIPORIGA); TString80 riga_contratto_codart = riga_contratto.get(RDOC_CODART); //solo le righe di tipo merce (verigh01) dei contratti devono comparire nelle NAC if (tiporiga == HARDY_TIPORIGA_MERCE) { const real riga_contratto_qta = riga_contratto.get_real(RDOC_QTA); const TString4 riga_contratto_um = riga_contratto.get(RDOC_UMQTA); const real riga_contratto_premio = riga_contratto.get_real(RC_1_PREMIO); // riga merce NAC TRiga_documento& nac_row = nac.new_row(tipo_riga_spp); nac_row.put(RDOC_CODART, cod_riga); //panegirico della descrizione nac_row.put(RDOC_DESCR, descr_riga_spp); nac_row.put(RDOC_DESCEST, msg_date); nac_row.put(RDOC_DESCLUNGA, "X"); //importi, qta, umqta nac_row.put(RDOC_UMQTA, riga_contratto_um); nac_row.put(RDOC_QTA, riga_contratto_qta); nac_row.put(RDOC_PREZZO, riga_contratto_premio); //iva nac_row.put(RDOC_CODIVA, codiva); //Imposta riga contratto di provenienza nac_row.set_original_rdoc_key(riga_contratto); } else //trattazione speciale delle righe spese (verigh02) (solo 'A'nticipi e 'R'ifatturazioni) if (tiporiga == HARDY_TIPORIGA_SOMMA && (tipo_contratto == 'A' || tipo_contratto == 'R')) { const real somma_anticipata = riga_contratto.get_real(RCA_2_ANTICIPATO); real somma_resa_storica = riga_contratto.get_real(RCA_2_RESO_STORICO); real somma_resa_corrente = riga_contratto.get_real(RCA_2_RESO_CORRENTE); const real esubero = somma_resa_storica + somma_resa_corrente - somma_anticipata; if (esubero > ZERO) { somma_resa_corrente -= esubero; //se c'è un esubero nel rimborso dell'anticipo esisterà solo una riga nella NAC, con una cifra pari alla somma.. //..che mancava a pareggiare l'anticipo (anche se l'esubero eccedesse tale differenza, sulla NAC ci va la somma a.. //..pareggio! così freghiamo qualcosa al cliente) nac.destroy_rows(); // riga spesa NAC TRiga_documento& nac_row = nac.new_row(tipo_riga_spp); nac_row.put(RDOC_CODART, cod_riga); //panegirico della descrizione nac_row.put(RDOC_DESCR, descr_riga_spp); nac_row.put(RDOC_DESCEST, msg_date); nac_row.put(RDOC_DESCLUNGA, "X"); //importi, qta nac_row.put(RDOC_QTA, 1); nac_row.put(RDOC_PREZZO, somma_resa_corrente); //iva nac_row.put(RDOC_CODIVA, codiva); //Imposta riga contratto di provenienza nac_row.set_original_rdoc_key(riga_contratto); } } } // salvataggi vari // --------------- // NAC // viene scritta comunque, con provv='D' se elaborazione definitiva int err = nac.write(); if (err == NOERR) { TString wrk_msg; wrk_msg.format(" -- Generata NAC: %-4s%-4s%c %7ld", (const char*)nac_codnum, (const char*)nac_tipo, provv, nac.get_long(DOC_NDOC)); log_msg << wrk_msg; } else log_msg << " -- Impossibile generare NAC !!"; // CONTRATTO //se il contratto è di tipo A/R aggiorna la riga spese speciale (verig02) if (err == NOERR) switch (tipo_contratto) { case 'A': case 'R': { FOR_EACH_PHYSICAL_RDOC(contratto, r, rdoc) { if (rdoc->get(RDOC_TIPORIGA) == HARDY_TIPORIGA_SOMMA) { const real anticipato = rdoc->get_real(RCA_2_ANTICIPATO); real reso_storico = rdoc->get_real(RCA_2_RESO_STORICO); reso_storico += rdoc->get_real(RCA_2_RESO_CORRENTE); rdoc->put(RCA_2_RESO_STORICO, reso_storico); //se il contratto è andato a pareggio avverte diligentemente l'operatore if (reso_storico >= anticipato) log_msg << " -- Contratto pareggiato!"; } //if (rdoc->get(RDOC_TIPORIGA)... } //FOR_EACH_PHYSICAL_RDOC(contratto... } break; case 'N': if (fatkey != NULL) { const char provv = fatkey->get_char(0); const int anno = fatkey->get_int(); const char* codnum = fatkey->get(); const long ndoc = fatkey->get_long(); const TDocumento fat(provv, anno, codnum, ndoc); genera_eff(fat, nac.totale_doc(), log); } break; default: break; } // se non ci sono errori -> in caso di elaborazione definitiva procede alla registrazione.. //.. del contratto (ricordiamo che in memoria il contratto ha già le righe aggiornate if (err == NOERR && definitivo && nac_ok) { //alla fine della fiera aggiorna il contratto err = contratto.rewrite(); if (err == NOERR) { log_msg << " -- Contratto aggiornato"; una_nac_definitiva = true; } else log_msg << " -- Impossibile aggiornare contratto: errore " << err; } if (err == NOERR) log.log(nac_ok ? 1 : 0, log_msg); else log.log(2, log_msg); log.log(0, ""); } //FOR_EACH_ARRAY_ITEM(... giro sui contratti cliente if (una_nac_definitiva) { const int tc = mask.get_int(F_TIPOCONTR); // 0x7 (tutti tranne nolo) o 0x8 (solo nolo) if (tc != 0x8) { const TString4 stato_finale = ini_get_string(CONFIG_DITTA, "ha", "StatoFinFatt"); FOR_EACH_ARRAY_ITEM(documenti_cliente, r, riga) { TDocumento& fattura = *(TDocumento*)riga; // fattura.put(DOC_STATO, stato_finale); fattura.rewrite(); } TString msg; msg.format(FR("Aggiornato stato elaborazione di %d fatture"), documenti_cliente.items()); log.log(0, msg); } } //il metodo ritornerà il successo o meno della registrazione return una_nac_definitiva; } bool THardy_elab_docs::genera_fat(const TMask& mask, TContratto_premi& contratto, TToken_string& fat_nolo, TLog_report& log) { //si informa se l'elaborazione è definitiva o meno const char provv = mask.get_bool(F_DEFINITIVO) ? 'D' : 'P'; const TDate datadoc = mask.get(F_DATAELAB); const TString4 fat_codnum = ini_get_string(CONFIG_DITTA, "ha", "NaNoloNum"); const TString4 fat_tipodoc = ini_get_string(CONFIG_DITTA, "ha", "FtNoloTip"); const TString4 fat_codpag = ini_get_string(CONFIG_DITTA, "ha", "CodPagFat"); const TString8 cod_spesa = ini_get_string(CONFIG_DITTA, "ha", "FtNoloSpe"); TDocumento fat(provv, datadoc.year(), fat_codnum, 0); //num_doc = 0 perchè viene aggiornato in fase di registrazione fat.set_tipo(fat_tipodoc); fat.put(DOC_STATO, 1); fat.put(DOC_DATADOC, datadoc); fat.put(DOC_TIPOCF, 'C'); fat.put(DOC_CODCF, contratto.get(DOC_CODCF)); fat.cli2doc(); fat.put(DOC_CODAG, contratto.get(DOC_CODAG)); fat.put(DOC_DOC1, contratto.get(DOC_NDOC)); fat.put(DOC_DATADOCRIF, contratto.get(DOC_DATADOC)); fat.put(DOC_CODPAG, fat_codpag); const TRectype& rec_spp = cache().get("SPP", cod_spesa); const TString80 descr_riga_spp = rec_spp.get("S0"); const TString4 codiva = rec_spp.get("S3"); const TString4 tipo_riga_spp = rec_spp.get("S8"); TString msg_rata; real importo = contratto.get_real(DOC_IMPPAGATO); const TDate recesso = contratto.data_recesso(); if (recesso.ok() && recesso <= mask.get_date(F_ADATA)) { const real rate_da_pagare = contratto.get_int(DOC_NCOLLI) - contratto.get_int(DOC_NUMANT); importo *= rate_da_pagare; contratto.put(DOC_NUMANT, contratto.get(DOC_NCOLLI)); // Chiude contratto forzosamente msg_rata = TR("\nRata di cessazione contratto"); } else { const TDate d = mask.get(F_ADATA); contratto.add(DOC_NUMANT, UNO); // Incrementa numero di rate pagate msg_rata << TR("\nRata ") << contratto.get(DOC_NUMANT) << '/' << contratto.get_int(DOC_NCOLLI) << ' ' << itom(d.month()) << ' ' << d.year(); } if (provv == 'D') { const int err = contratto.rewrite(); if (err != NOERR) log.log(2, TR("Impossibile aggiornare il numero di rate pagate sul contratto")); } TRiga_documento& rdoc = fat.new_row(tipo_riga_spp); rdoc.put(RDOC_CODART, cod_spesa); rdoc.put(RDOC_DESCR, descr_riga_spp); rdoc.put(RDOC_DESCEST, msg_rata); rdoc.put(RDOC_DESCLUNGA, "X"); rdoc.put(RDOC_QTA, UNO); rdoc.put(RDOC_PREZZO, importo); rdoc.put(RDOC_CODIVA, codiva); const int err = fat.write(); if (err == NOERR) { TString log_msg; log_msg.format("Generata FATTURA: %-4s%-4s%c %7ld", (const char*)fat_codnum, (const char*)fat_tipodoc, fat.get_char(DOC_PROVV), fat.get_long(DOC_NDOC)); log.log(0, log_msg); fat_nolo = fat.get(DOC_PROVV); fat_nolo.add(fat.get(DOC_ANNO)); fat_nolo.add(fat.get(DOC_CODNUM)); fat_nolo.add(fat.get(DOC_NDOC)); fat_nolo.add(fat.get(DOC_DATADOC)); } else { log.log(2, TR("Impossibile generare la FATTURA")); } return err == NOERR; } bool THardy_elab_docs::genera_eff(const TDocumento& fat, const real& tot_nac, TLog_report& log) { #ifdef NDEBUG if (fat.get_char(DOC_PROVV) != 'D') return false; #endif const real tot_fat = fat.totale_doc(); if (tot_fat == tot_nac) return false; const bool credito = tot_fat > tot_nac; const real importo = credito ? (tot_fat - tot_nac) : (tot_nac - tot_fat); TDate datascad = fat.get(DOC_DATADOC); datascad.set_end_month(); TEffetto eff; eff.put(EFF_DATASCAD, datascad); eff.put(EFF_TIPOPAG, credito ? tp_rid : tp_bonifico); eff.put(EFF_TIPOCF, fat.get(DOC_TIPOCF)); eff.put(EFF_CODCF, fat.get(DOC_CODCF)); eff.put(EFF_CODABI, fat.get(DOC_CODABIA)); eff.put(EFF_CODCAB, fat.get(DOC_CODCABA)); TRectype& reff = eff.row_r(1, true); reff.put(REFF_ANNO, fat.get(DOC_ANNO)); reff.put(REFF_NUMPART, fat.get(DOC_NDOC)); reff.put(REFF_NRIGA, 1); reff.put(REFF_NRATA, 1); reff.put(REFF_PROVV, fat.get(DOC_PROVV)); reff.put(REFF_ANNODOC, fat.get(DOC_ANNO)); reff.put(REFF_CODNUM, fat.get(DOC_CODNUM)); reff.put(REFF_NFATT, fat.get(DOC_NDOC)); reff.put(REFF_DATAFATT,fat.get(DOC_DATADOC)); reff.put(REFF_IMPFATT, tot_fat); reff.put(REFF_IMPORTO, importo); reff.put(REFF_ACCSAL, 'A'); TLocalisamfile file(LF_EFFETTI); int err = eff.write(file); if (err == NOERR) { TString msg; msg << "Registrato effetto " << eff.numero(); log.log(0, msg); } else log.log(2, "Impossibile registrare l'effetto"); return err == NOERR; } void THardy_elab_docs::elabora_documenti(const TMask& mask, TISAM_recordset& fatture, TLog_report& log) { TProgind pi(fatture.items(), TR("Elaborazione documenti in corso...")); //inizializza variabili da usare nella scansione del recordset long old_codcf = 0L; //array con l'insieme dei contratti e dei documenti elaborati per un singolo cliente! TArray contratti_cliente, documenti_cliente; TAssoc_array fat_nolo; const int tc = mask.get_int(F_TIPOCONTR); // 0x7 (tutti tranne nolo) o 0x8 (solo nolo) const char fr = (tc & 0x8) ? 'M' : mask.get(F_FREQUENZA)[0]; if (tc & 0x8) // Elaborazione nolo? { // Elabora tutti i contratti di nolo per generare fatture ed effetti const word freq = find_contratti_cliente(mask.get_long(F_CODCF), mask, 0x8, fr, contratti_cliente); if (freq) // Nolo sarebbe solo mensile -> if (freq == 0x01) { FOR_EACH_ARRAY_ROW(contratti_cliente, n_nolo, r_nolo) { TContratto_premi& contratto = *(TContratto_premi*)r_nolo; TToken_string fatkey; if (genera_fat(mask, contratto, fatkey, log)) { TToken_string cnkey; // Chiave contratto cnkey.add(contratto.get(DOC_PROVV)); cnkey.add(contratto.get(DOC_ANNO)); cnkey.add(contratto.get(DOC_CODNUM)); cnkey.add(contratto.get(DOC_NDOC)); fat_nolo.add(cnkey, fatkey); } } } } // giro sulle fatture (è il giro di più alto livello che viene esteso all'interno delle elabora_contratti) const TDate data_fine = mask.get(F_ADATA); for (bool ok = fatture.move_first(); ok; ok = fatture.move_next()) { if (!pi.addstatus(1)) break; const long codcf = fatture.get(DOC_CODCF).as_int(); // al cambio cliente deve controllare i contratti di quel cliente nel periodo di elaborazione!! if (codcf != old_codcf) { //generazione NAC del cliente precedente (una per contratto cliente) if (old_codcf > 0 && !documenti_cliente.empty()) genera_nac(mask, contratti_cliente, fat_nolo, documenti_cliente, log); //aggiorna old_codcf in modo da poter controllare i contratti solo al cambio codcf old_codcf = codcf; documenti_cliente.destroy(); const word freq = find_contratti_cliente(codcf, mask, tc, fr, contratti_cliente); if (freq != 0) { TString8 key; key.format("C|%ld", codcf); TString msg; msg << TR("Cliente") << ' ' << codcf << ' ' << cache().get(LF_CLIFO, key, CLI_RAGSOC); log.log(0, msg); } } if (!contratti_cliente.empty()) // Test sulla presenza di contratti { const TRectype& head = fatture.cursor()->curr(); //se ha trovato uno o più contratti validi nel periodo passa alla elaborazione dei documenti del cliente TDocumento* curr_doc = new TDocumento(head); // elabora il documento corrente aggiornando le somme restituite sui contratti validi const word freq = elabora_contratti(*curr_doc, contratti_cliente, data_fine, log); if (freq != 0) documenti_cliente.add(curr_doc); else delete(curr_doc); } } //for (bool ok = recset.move_first()... //generazione NAC dell'ultimo cliente (una per contratto cliente) if (!contratti_cliente.empty() && !documenti_cliente.empty()) { genera_nac(mask, contratti_cliente, fat_nolo, documenti_cliente, log); documenti_cliente.destroy(); } } //metodo di alto livello con i punti principali del programma (come da analisi...) void THardy_elab_docs::elabora(const TMask& mask) { //1) eventuale accoppamento di tutti i documenti provvisori creati con precedenti elaborazioni precedenti int nac_killed = 0; if (mask.get_bool(F_KILLPROVV)) nac_killed = kill_provv_nac(mask); //log report con segnalazioni sui clienti trattati (bene o male) TLog_report log(TR("Sintesi elaborazione")); log.log(0, ""); //2) recordset ordinato codag-codcf-numdoc con tutti i docs che soddisfano i parametri dell'utente // --------------------------------------------------------------------------------------------- TISAM_recordset recset(""); const long items = genera_recordset(mask, recset); if (items == 0) log.log(1, TR("Non esistono documenti di vendita che soddisfino i parametri selezionati!")); //3) elaborazione documenti e contratti, generazione NAC, salvataggi // --------------------------------------------------------------- elabora_documenti(mask, recset, log); //3) scrittura log // ------------- log.print_or_preview(); } void THardy_elab_docs::main_loop() { THardy_elab_docs_mask mask; while (mask.run() == K_ENTER) elabora(mask); } bool THardy_elab_docs::create() { open_files(LF_TABCOM, LF_TAB, LF_CLIFO, LF_DOC, LF_RIGHEDOC, LF_ANAMAG, 0); return TSkeleton_application::create(); } int ha0500 (int argc, char* argv[]) { THardy_elab_docs elabapp; elabapp.run(argc, argv, TR("Generazione NAC")); return 0; }