#include #include #include "ca2.h" #include "ca2100a.h" #include "calib01.h" #include "../cg/cglib01.h" #include #include #include "movana.h" #include "rmovana.h" #include "rrip.h" #include "saldana.h" #include "commesse.h" class TMovanal_msk : public TAutomask { TAssoc_array _saldi; bool _use_pdc; short _cms_start; short _cms_end; short _cdc_start; short _cdc_end; TCache_ripartizioni _cache_rip; protected: virtual bool on_field_event(TOperable_field& o, TField_event fe, long jolly); virtual bool can_be_closed() const; virtual bool on_key(KEY key); const TToken_string& rec2key(const TRectype& rec) const; void aggiorna_saldo_riga(int r); // Lettura movimento contabile TToken_string& get_rip_row(const TRectype& rrip); void split_cg_row(const TRectype& row, const TAnal_ripartizioni_batch& rrip); bool multi_split_cg_row(const TRectype& row, const TAnal_ripartizioni_batch& rrip); void load_cg_row(const TRectype& row); void load_cg_mov(); bool is_ripartible_movcg(bool& has_multirip); int create_sheet_fields(int lf, int& y, short& dlg, bool required); void create_sheet(); int get_constant_columns(TToken_string& tok) const; public: void reset_saldi(); TImporto& saldo(const TRectype& row); void show_locked_buttons(); bool row2rec(int row, TRectype& rec) const; void row2imp(const TToken_string& row, TImporto& imp) const; bool row2imp(int r, TImporto& imp) const; void imp2row(const TImporto& imp, TToken_string& row) const; TToken_string& rec2row(const TRectype& rec, int i) const; TMovanal_msk(); virtual ~TMovanal_msk() { } }; class TMovanal_app : public TRelation_application { TRelation* _rel; TMovanal_msk* _msk; protected: virtual const char* extra_modules() const { return "cm"; } //deve funzionare anche per le commesse virtual bool user_create(); virtual bool user_destroy(); virtual TMask* get_mask(int); virtual TRelation* get_relation() const; virtual bool get_next_key(TToken_string& key); void write_rows(const TMask& m); void read_rows(const TMask& m); virtual bool protected_record(TRectype& rec); virtual int write(const TMask& m); virtual int rewrite(const TMask& m); virtual int read(TMask& m); virtual void ini2sheet(TConfig& ini, TSheet_field& sheet); virtual void sheet2ini(TSheet_field& sheet,TConfig& ini); virtual void init_query_mode(TMask& m); virtual void init_insert_mode(TMask& m); virtual void init_modify_mode(TMask& m); }; static TMovanal_app& app() { return (TMovanal_app&)main_app(); } /////////////////////////////////////////////////////////// // TMovanal_msk /////////////////////////////////////////////////////////// bool TMovanal_msk::can_be_closed() const { const bool lck = app().is_transaction() && mode() == MODE_INS; return lck ? false : TAutomask::can_be_closed(); } bool TMovanal_msk::on_key(KEY key) { if (key == K_SHIFT + K_F12) { enable(F_BLOCCATO); return true; } return TAutomask::on_key(key); } void TMovanal_msk::show_locked_buttons() { const bool is_trans = app().is_transaction(); const bool modifying = mode() == MODE_MOD; const bool inserting = mode() == MODE_INS; const bool editing = modifying || inserting; const bool unlocked = !(modifying && get_bool(F_BLOCCATO)); show (F_BLOCCATO, modifying); enable(F_BLOCCATO, unlocked); enable(DLG_SAVEREC, unlocked && editing); enable(DLG_DELREC, unlocked && !inserting); enable(DLG_QUIT, !is_trans || !inserting); enable(DLG_CANCEL, !is_trans || !inserting); enable(F_RESET, unlocked && editing); } TToken_string& TMovanal_msk::get_rip_row(const TRectype& rrip) { TSheet_field& sheet = sfield(F_RIGHE); TToken_string keyrip; keyrip.add(rrip.get(RRIP_CODCOSTO)); keyrip.add(rrip.get(RRIP_CODCMS)); keyrip.add(rrip.get(RRIP_CODFASE)); keyrip.add(rrip.get(RRIP_CODCONTO)); TRectype rmov(LF_RMOVANA); const int totrows = sheet.items(); if (totrows > 0) { TToken_string keyrow; for (int i = 0; i < totrows; i++) { row2rec(i, rmov); keyrow.cut(0); keyrow.add(rmov.get(RMOVANA_CODCCOSTO)); keyrow.add(rmov.get(RMOVANA_CODCMS)); keyrow.add(rmov.get(RMOVANA_CODFASE)); keyrow.add(rmov.get(RMOVANA_CODCONTO)); if (keyrow == keyrip) return sheet.row(i); // Ho trovato una riga compatibile } } // Devo creare una riga nuova rmov.zero(); rmov.put(RMOVANA_CODCCOSTO, keyrip.get(0)); rmov.put(RMOVANA_CODCMS, keyrip.get()); rmov.put(RMOVANA_CODFASE, keyrip.get()); rmov.put(RMOVANA_CODCONTO, keyrip.get()); TToken_string& row = rec2row(rmov, -1); return row; } void TMovanal_msk::split_cg_row(const TRectype& row, const TAnal_ripartizioni_batch& rrip) { // Importo totale da distribuire arrotondato ai decimali della valuta di conto TGeneric_distrib distrib(row.get_real(RMV_IMPORTO), TCurrency::get_firm_dec()); // Calcolo tutte le percentuali da ripartire int i; for (i = 1; i <= rrip.rows(); i++) { const real quota = rrip[i].get_real(RRIP_RIPARTO); distrib.add(quota); } TString80 desc; // Variabile di appoggio per descrizione riga for (i = 1; i <= rrip.rows(); i++) { TToken_string& riga = get_rip_row(rrip[i]); // Cerca o crea la riga cui sommare la quota TImporto impriga; row2imp(riga, impriga); // Legge l'importo della riga const TImporto imp(row.get_char(RMV_SEZIONE), distrib.get()); // Legge la quota da distribuire impriga += imp; impriga.normalize(); // Incrementa l'importo della riga e lo normalizza imp2row(impriga, riga); // Aggiorna la riga riga.get(2, desc); // Legge descrizione riga corrente if (desc.blank()) // Se e' vuota allora ... riga.add(row.get(RMV_DESCR), 2); // ... ci copio la descrizione della riga contabile } } bool TMovanal_msk::multi_split_cg_row(const TRectype& row, const TAnal_ripartizioni_batch& rrip) { bool splitted = true; //crea l'array con tutte le ripartizioni compatibili (sorelle) //1) crea lo sheet con codice e descrizione delle ripartizioni possibile TArray_sheet lista_rips(-1, -1, 64, 16, TR("Scelta ripartizioni"), HR("Codice@8|Descrizione@50"), 0, 6); //2) trova quante sono le ripartizioni possibili const long nrips = rrip.find_sister_rips(lista_rips.rows_array()); //3a) se le ripartizioni sono più di una mostra una maschera di scelta generata al volo con lo sheet dell'elenco.. //..ripartizioni possibili e i dati della riga che propone il dilemma di scelta; la riga può essere contabile o.. //..analitica in base al contesto in cui viene chiamata la multi_split_cg_row() if (nrips > 1) { //campi comuni ai 2 casi lista_rips.add_number(201, 0, "N.Riga ", 1, 0, 3, "D"); lista_rips.add_string(202, 0, "Descriz. ", 1, 1, 50, "D"); lista_rips.add_string(209, 0, "Importo ", 1, 3, 1, "D"); lista_rips.add_currency(210, 0, "", 14, 3, 18, "D"); switch (row.num()) { case LF_RMOV: //metodo chiamato in fase di registrazione del movimento in CG { lista_rips.add_number(203, 0, "Gruppo ", 1, 2, 3, "D"); lista_rips.add_number(204, 0, "Conto ", 16, 2, 3, "D"); lista_rips.add_number(205, 0, "Sottoconto ", 30, 2, 6, "D"); lista_rips.set(201, row.get(RMV_NUMRIG)); lista_rips.set(202, row.get(RMV_DESCR)); lista_rips.set(203, row.get(RMV_GRUPPO)); lista_rips.set(204, row.get(RMV_CONTO)); lista_rips.set(205, row.get(RMV_SOTTOCONTO)); lista_rips.set(209, row.get(RMV_SEZIONE)); lista_rips.set(210, row.get(RMV_IMPORTO)); } break; case LF_RMOVANA: //metodo chiamato in fase di registrazione del movimento in CA { lista_rips.add_string(203, 0, "Conto ", 1, 2, 20, "D"); lista_rips.add_string(204, 0, "Commessa ", 1, 4, 20, "D"); lista_rips.add_string(205, 0, "C. Costo ", 1, 5, 20, "D"); lista_rips.set(201, row.get(RMOVANA_NUMRIG)); lista_rips.set(202, row.get(RMOVANA_DESCR)); lista_rips.set(203, row.get(RMOVANA_CODCONTO)); lista_rips.set(204, row.get(RMOVANA_CODCMS)); lista_rips.set(205, row.get(RMOVANA_CODCCOSTO)); lista_rips.set(209, row.get(RMOVANA_SEZIONE)); lista_rips.set(210, row.get(RMOVANA_IMPORTO)); } break; default: break; } splitted = lista_rips.run() == K_ENTER; if (splitted) { TAnal_ripartizioni_batch selected_rip; selected_rip.read('I', lista_rips.row().get(0)); split_cg_row(row, selected_rip); } } else split_cg_row(row, rrip); return splitted; } bool TMovanal_msk::is_ripartible_movcg(bool& has_multirip) { bool is_ripartible_movcg = true; const TString& numregcg = get(F_NUMREGCG); TRecord_array cg(numregcg, LF_RMOV); TString query; for (int r = cg.last_row(); r > 0; r--) { const TRectype& row = cg.row(r); const TBill zio(row); if (zio.is_analitico()) { //se il conto è valido per l'analitica cerca,nell'annoes indicato, delle ripartizioni pertinenti a tale conto const int annoes = row.get_int(RMV_ANNOES); const TAnal_ripartizioni_batch& rrip = _cache_rip.righe_interattive(zio, annoes); const long rrip_items = rrip.rows(); //solo per conti analitici (!_use_pdc) if (!_use_pdc && rrip_items <= 0) // Non trovato una ripartizione valida per il povero conto { is_ripartible_movcg = error_box(FR("Non esiste una ripartizione del conto %d.%d.%ld\n" "presente sulla riga %d del movimento %ld"), zio.gruppo(), zio.conto(), zio.sottoconto(), r, atol(numregcg)); break; } //if(rrip_items.. //controlla se ci sono più ripartizioni sorelle; in questo caso deve avvisare chi lo chiama di questo fatto.. //..in modo da non utilizzare il metodo delle colonne costanti; alla prima multiripartizione deve uscire perchè.. //..is_ripartible_movcg sarà true (se ho una multiripartizione allora è ripartibile) e has_multirip potrebbe.. //..tornare a false nella riga successiva has_multirip = rrip.has_multirip(); if (_use_pdc && has_multirip) return is_ripartible_movcg; } //if(zio.is_analitico.. } //for(int r=cg.last_row.. return is_ripartible_movcg; } void TMovanal_msk::load_cg_row(const TRectype& row) { //se il conto è valido per l'analitica cerca,nell'annoes indicato, delle ripartizioni pertinenti a tale conto const TBill zio(row); if (zio.is_analitico()) { const int annoes = row.get_int(RMV_ANNOES); const TAnal_ripartizioni_batch& rrip = _cache_rip.righe_interattive(zio, annoes); const long rrip_items = rrip.rows(); bool splitted = rrip_items > 0; if (splitted) { //se sa che ci sono varie ripartizioni interattive relative a questo conto deve trovarne la lista e cercare la.. //..più adatta all'utonto if (rrip.has_multirip()) splitted = multi_split_cg_row(row, rrip); else //se invece c'è una sola ripartizione possibile la applica e via! split_cg_row(row, rrip); } if (!splitted) // Non ho trovato nessuno schema di ripartizione valido { // Creo una riga nuova TSheet_field& sheet = sfield(F_RIGHE); TToken_string& riga = sheet.row(-1); const TImporto imp(row.get_char(RMV_SEZIONE), row.get_real(RMV_IMPORTO)); imp2row(imp, riga); // Ci copio l'importo riga.add(row.get(RMV_DESCR), 2); // e la descrizione della riga contabile if (_use_pdc) { TMask_field* f = sheet.sheet_mask().find_by_fieldname(RMOVANA_CODCONTO); const int pos = sheet.cid2index(f->dlg()); TString8 str; str = row.get(RMV_GRUPPO); str.right_just(3, '0'); riga.add(str, pos-2); str = row.get(RMV_CONTO); str.right_just(3, '0'); riga.add(str, pos-1); str = row.get(RMV_SOTTOCONTO); str.right_just(6, '0'); riga.add(str, pos); } } //else di rrip_items > 0... } //if(zio.is_analitico(... } // A partire dal movimento contabile calcola il totale documento ed eventualmente prepara lo sheet void TMovanal_msk::load_cg_mov() { const TString& numregcg = get(F_NUMREGCG); const TRectype& mov = cache().get(LF_MOV, numregcg); set(F_DATAREG, mov.get(MOV_DATAREG)); if (mode() == MODE_INS) { set(F_DATACOMP, mov.get(MOV_DATACOMP)); set(F_DESCR, mov.get(MOV_DESCR)); } set(F_CODCAUS, mov.get(MOV_CODCAUS), 0x2); set(F_NUMDOC, mov.get(MOV_NUMDOC)); set(F_DATADOC, mov.get(MOV_DATADOC)); set(F_TIPODOC, mov.get(MOV_TIPODOC)); set(F_TIPOCF, mov.get(MOV_TIPO)); set(F_CODCF, mov.get(MOV_CODCF), 0x3); TImporto totdoc; TRecord_array cg(numregcg, LF_RMOV); const bool autoinsert = sfield(F_RIGHE).empty(); for (int i = 1; i <= cg.rows(); i++) { const TRectype& row = cg.row(i); const TBill zio(row); if (zio.is_analitico()) { const TImporto imp(row.get_char(RMV_SEZIONE), row.get_real(RMV_IMPORTO)); totdoc += imp; if (autoinsert) load_cg_row(row); } } set(F_TOTDOC, totdoc.valore()); const char sez[2] = { totdoc.sezione(), '\0' }; set(F_SEZIONE, sez); } int TMovanal_msk::get_constant_columns(TToken_string& tok) const { int n = 0; TSheet_field& sf = sfield(F_RIGHE); const int items = sf.items(); if (items > 0) { TMask& m = sf.sheet_mask(); for (int f = 0; f < m.fields(); f++) { const TMask_field& mf = m.fld(f); const TFieldref* fr = mf.field(); if (fr != NULL && !fr->name().starts_with(RMOVANA_CODCONTO)) { const int i = sf.cid2index(mf.dlg()); if (i >= 2) { const TString campione = sf.row(0).get(i); if (campione.full()) { int j; for (j = items-1; j > 0; j--) { if (campione != sf.row(j).get(i)) break; } if (j == 0) { tok.add(campione, i); n++; } } } } } } return n; } bool TMovanal_msk::on_field_event(TOperable_field& o, TField_event e, long jolly) { static TString __codcms; switch (o.dlg()) { case F_DATAREG: if ((e == fe_modify || e == fe_close) && !query_mode()) { const TDate datareg = o.get(); const TEsercizi_contabili ec; if (ec.date2esc(datareg) <= 0) return error_box(((TEdit_field&)o).get_warning()); if (e == fe_close && field(F_DATACOMP).empty()) set(F_DATACOMP, datareg); } break; case F_DATACOMP: if ((e == fe_modify || e == fe_close) && !query_mode()) { const TDate datareg = get(F_DATAREG); TDate datacomp = o.get(); if (!datacomp.ok()) datacomp = datareg; const bool preventivo = get(F_TIPO).full(); //i movimenti normali devono avere data competenza nel presente o passato!! if (!preventivo && datacomp > datareg) return error_box(TR("La data di competenza non puo' superare la data di registrazione")); const TEsercizi_contabili ec; int ae = ec.date2esc(datacomp); //movimenti preventivi in esercizi futuri if (ae <= 0 && preventivo && datacomp > datareg) ae = ec.date2esc(datareg) + datacomp.year() - datareg.year(); if (ae > 0) set(F_ANNOES, ae, 0x1); else return error_box(((TEdit_field&)o).get_warning()); const int ar = ec.date2esc(datareg); const int ap = ec.pred(ar); //preventivi un anno indietro, nel presente o nel futuro if (preventivo) { if (ae < ap) return error_box(FR("La data di competenza non può precedere l'esercizio %d"), ap); } else //normali solo un anno indietro o nel presente { if (ae != ar && ae != ap) return error_box(FR("La data di competenza deve appartenere all'esercizio in corso o al precedente")); } } break; case F_DATAFCOMP: if (e == fe_modify || e == fe_close) { const TDate datacomp = get(F_DATACOMP); const TDate datafcomp = o.get(); if (datafcomp.ok()) { if (datafcomp < datacomp) return error_box(((TEdit_field&)o).get_warning()); } else o.set(datacomp.string()); //se la data fine competenza viene lasciata vuota -> e' uguale alla datacomp } break; case F_RIGHE: if (e == se_enter) { const int r = int(jolly); aggiorna_saldo_riga(r); } if (e == se_query_modify) { TSheet_field & s = sfield(F_RIGHE); const int r = int(jolly); __codcms.cut(0); if (_cms_start > 0) for (short j = _cms_start; j <= _cms_end; j++) __codcms << s.row(r).get(s.cid2index(j)); } if (e == se_notify_modify) { // Ho la gestione commesse al primo livello ed i centri di costo al secondo? if (_cms_start > 0 && _cms_start < _cdc_start) { const int r = int(jolly); TSheet_field & s = sfield(F_RIGHE); TMask & m = s.sheet_mask(); const TRectype & curr = ((TEdit_field &) m.field(_cms_start)).browse()->cursor()->curr(); const TString & codcms = curr.get(COMMESSE_CODCMS); // Cerco di proporre il centro di costo di default sulla commessa if (__codcms != codcms) { const TString codcosto = curr.get(COMMESSE_CODCOSTO); if (codcosto.full()) { for (short i = _cdc_start; i <= _cdc_end; i++) { TEdit_field & e = m.efield(i); const TFieldref * f = e.field(); if (f != NULL) { const int from = f->from(); const int to = f->to(); m.set(i, codcosto.sub(from, to), 0x2); s.update_row(r); s.force_update(r); } } } } } } if (e == fe_close) { const TImporto totdoc(get(F_SEZIONE)[0], get_real(F_TOTDOC)); TImporto totrig; TSheet_field& sf = sfield(F_RIGHE); FOR_EACH_SHEET_ROW(sf, i, row) { TImporto imp; row2imp(*row, imp); totrig += imp; //controlli sullo sheet in uscita!! Servono in quanto, anche scrivendo cazzate, l'utente potrebbe.. //..registrare lo stesso il movimento cliccando su FINE e rispondendo SI alla successiva richiesta.. //..di conferma registrazione! if (sf.dirty()) { sf.check_row(i, 0); //si usa 0 per simulare la row2mask senza check aggiuntivi che potrebbero sputtanare qualche campo (es. commessa al crpa) TMask& rm = sf.sheet_mask(); FOR_EACH_MASK_FIELD(rm, j, fld) { if (fld->has_check() && !fld->check(FINAL_CHECK)) { TString msg; msg << TR("Valore errato nel campo ") << fld->prompt(); return error_box(msg); } } } } if (totrig != totdoc) return error_box(FR("Il totale delle righe e' %s %s, mentre il totale documento e' %s %s"), totrig.valore().string(), totrig.sezione() == 'D' ? TR("Dare") : TR("Avere"), totdoc.valore().string(), totdoc.sezione() == 'D' ? TR("Dare") : TR("Avere")); } break; case S_DARE: case S_AVERE: if (e == fe_modify) { TSheet_field& sf = sfield(F_RIGHE); if (!o.empty()) o.mask().reset(o.dlg() == S_DARE ? S_AVERE : S_DARE); const int r = sfield(F_RIGHE).selected(); sf.update_row(r); aggiorna_saldo_riga(r); } break; case F_NUMREGCG: if (e == fe_init) { const bool vuoto = o.empty(); enable(-5, vuoto); if (!vuoto) load_cg_mov(); } break; case F_BLOCCATO: if (e == fe_modify) { enable(DLG_SAVEREC); // Altrimenti non riesco a registrare la variazione di stato } break; case F_RESET: if (e == fe_button) { bool has_multirip = false; if (is_ripartible_movcg(has_multirip)) { TToken_string constants; const int cc = get_constant_columns(constants); TSheet_field& sf = sfield(F_RIGHE); sf.destroy(); load_cg_mov(); if (cc > 0 && !has_multirip) // Se ci sono colonne costanti le recupera da quelle vecchie salvate { TString campione; FOR_EACH_SHEET_ROW(sf, i, row) { for (int i = constants.items()-1; i > 0; i--) { constants.get(i, campione); if (!campione.blank()) row->add(campione, i); } } } sf.force_update(); } else disable(F_RESET); } break; default: if (e == fe_modify && o.dlg() >= S_CDC1 && o.dlg() <= S_CON4) { TSheet_field& sf = sfield(F_RIGHE); const int r = sf.selected(); sf.update_row(r); aggiorna_saldo_riga(r); } break; } return true; } // Prende dalla riga r dello sheet i dati da scrivere nel record rec dei movimenti analitici bool TMovanal_msk::row2rec(int r, TRectype& rec) const { TSheet_field& sheet = sfield(F_RIGHE); if (r < 0 || r >= sheet.items()) return false; rec.put(RMOVANA_ANNOES, get(F_ANNOES)); rec.put(RMOVANA_DATACOMP, get(F_DATACOMP)); TToken_string& row = sheet.row(r); const real dare = row.get(sheet.cid2index(S_DARE)); const real avere = row.get(); if (dare.is_zero()) { rec.put(RMOVANA_SEZIONE, 'A'); rec.put(RMOVANA_IMPORTO, avere); } else { rec.put(RMOVANA_SEZIONE, 'D'); rec.put(RMOVANA_IMPORTO, dare); } TMask& sm = sheet.sheet_mask(); for (int i = sm.fields()-1; i >= 0; i--) { const TMask_field& mf = sm.fld(i); const TFieldref* fr = mf.field(); if (fr != NULL) { const int idx = sheet.cid2index(mf.dlg()); const char* s = row.get(idx); if (s != NULL) fr->write(s, rec); } } return true; } void TMovanal_msk::imp2row(const TImporto& imp, TToken_string& row) const { row.add(imp.sezione() == 'D' ? imp.valore().string() : "", 0); row.add(imp.sezione() == 'A' ? imp.valore().string() : "", 1); } TToken_string& TMovanal_msk::rec2row(const TRectype& rec, int r) const { CHECK(rec.num() == LF_RMOVANA, "Bad record type"); TSheet_field& sheet = sfield(F_RIGHE); TMask& sm = sheet.sheet_mask(); TToken_string& row = sheet.row(r); const TImporto imp(rec.get_char(RMOVANA_SEZIONE), rec.get_real(RMOVANA_IMPORTO)); imp2row(imp, row); // Carica tutti gli altri campi dopo le colonne Dare/Avere for (int i = sm.fields()-1; i >= 0; i--) { TMask_field& mf = sm.fld(i); const TFieldref* fr = mf.field(); if (fr != NULL) { const int idx = sheet.cid2index(mf.dlg()); row.add(fr->read(rec), idx); } } return row; } void TMovanal_msk::row2imp(const TToken_string& row, TImporto& imp) const { real dare; row.get(0, dare); real avere; row.get(1, avere); if (dare.is_zero()) imp.set('A', avere); else imp.set('D', dare); } bool TMovanal_msk::row2imp(int r, TImporto& imp) const { TSheet_field& sf = sfield(F_RIGHE); if (r >= 0 && r < sf.items()) { const TToken_string& row = sf.row(r); row2imp(row, imp); } else imp.set('D', ZERO); return !imp.is_zero(); } const TToken_string& TMovanal_msk::rec2key(const TRectype& rec) const { TToken_string& key = get_tmp_string(); key = get(F_TIPO); key.add(rec.get(RMOVANA_ANNOES)); key.add(rec.get(RMOVANA_CODCCOSTO)); key.add(rec.get(RMOVANA_CODCMS)); key.add(rec.get(RMOVANA_CODFASE)); key.add(rec.get(RMOVANA_CODCONTO)); return key; } void TMovanal_msk::reset_saldi() { _saldi.destroy(); } TImporto& TMovanal_msk::saldo(const TRectype& rec) { const TToken_string& key = rec2key(rec); TImporto* imp = (TImporto*)_saldi.objptr(key); if (imp == NULL) { const TRectype& saldo = cache().get(LF_SALDANA, key); const char* fld_sez = NULL; const char* fld_val = NULL; switch (get(F_TIPO)[0]) { case 'P': fld_sez = SALDANA_SEZIONEP; fld_val = SALDANA_SALDOP; break; case 'V': fld_sez = SALDANA_SEZIONEV; fld_val = SALDANA_SALDOV; break; default : fld_sez = SALDANA_SEZIONE; fld_val = SALDANA_SALDO; break; } imp = new TImporto(saldo.get_char(fld_sez), saldo.get_real(fld_val)); _saldi.add(key, imp); } return *imp; } void TMovanal_msk::aggiorna_saldo_riga(int r) { TRectype rec(LF_RMOVANA); row2rec(r, rec); const TToken_string& key = rec2key(rec); TImporto sld = saldo(rec); TSheet_field& sheet = sfield(F_RIGHE); FOR_EACH_SHEET_ROW(sheet, i, row) { row2rec(i, rec); const TToken_string& k = rec2key(rec); if (k == key) { real dare; row->get(0, dare); real avere; row->get(1, avere); if (dare.is_zero()) sld += TImporto('A', avere); else sld += TImporto('D', dare); } } sld.normalize(); set(F_DARE, sld.sezione() == 'D' ? sld.valore() : ZERO); set(F_AVERE, sld.sezione() == 'A' ? sld.valore() : ZERO); } int TMovanal_msk::create_sheet_fields(int lf, int& y, short& dlg, bool required) { TSheet_field& sf = sfield(F_RIGHE); TMask& sm = sf.sheet_mask(); const int h = ca_create_fields(sm, 0, lf, 1, y, dlg, dlg+50); for (int i = 0; i < h; i++) { TEdit_field& fld = sm.efield(dlg+i); int logic = lf; if (logic == LF_FASI) { const TMultilevel_code_info& fasinfo = ca_multilevel_code_info(LF_FASI); if (fasinfo.parent() != 0) { const TMultilevel_code_info& parinfo = ca_multilevel_code_info(fasinfo.parent()); if (i < parinfo.levels()) logic = fasinfo.parent(); } } if (logic == LF_PCON) { const TFieldref* f = fld.field(); const TString& fieldname = f->name(); if (fieldname == "GRUPPO") fld.set_field("CODCONTO[1,3]"); else if (fieldname == "CONTO") fld.set_field("CODCONTO[4,6]"); else if (fieldname == "SOTTOCONTO") fld.set_field("CODCONTO[7,12]"); } else { const char* fieldname = NULL; switch(logic) { case LF_CDC : fieldname = RMOVANA_CODCCOSTO; break; case LF_COMMESSE: fieldname = RMOVANA_CODCMS; break; case LF_FASI : fieldname = RMOVANA_CODFASE; break; default : fieldname = RMOVANA_CODCONTO; break; } TFieldref* f = (TFieldref*)fld.field(); f->set_name(fieldname); } fld.check_type(required ? CHECK_REQUIRED : CHECK_NORMAL); TEdit_field& dfld = sm.efield(dlg+50+i); dfld.set_field(EMPTY_STRING); // Toglie campi che fan saltare gli output! } y += h+1; dlg += h; return h; } //metodo per la creazione delle righe dello sheet con le righe movimento analitico //si basa sulla configurazione void TMovanal_msk::create_sheet() { TSheet_field& sf = sfield(F_RIGHE); TMask& sm = sf.sheet_mask(); sm.hide(-1); const TMultilevel_code_info& fasinfo = ca_multilevel_code_info(LF_FASI); TConfig& ini = ca_config(); const bool fsc_req = ini.get_bool("FscRequired"); int y = 3; short dlg = S_CDC1+100; // id del primo campo da generare _cdc_start = _cdc_end = -1; _cms_start = _cms_end = -1; for (int i = 0; i < 2; i++) { const TString& level = ini.get("Level", NULL, i+1); // Legge il livello 1 o 2 if (level == "CDC") // Crea centro di costo { _cdc_start = dlg; _cdc_end = dlg + ca_multilevel_code_info(LF_CDC).levels()-1; if (fasinfo.parent() == LF_CDC) create_sheet_fields(LF_FASI, y, dlg, fsc_req); else { const bool cdc_req = ini.get_bool("CdcRequired"); create_sheet_fields(LF_CDC, y, dlg, cdc_req); } } else if (level == "CMS") // Crea commessa { _cms_start = dlg; _cms_end = dlg + ca_multilevel_code_info(LF_COMMESSE).levels()-1; if (fasinfo.parent() == LF_COMMESSE) create_sheet_fields(LF_FASI, y, dlg, fsc_req); else { const bool cms_req = ini.get_bool("CmsRequired"); create_sheet_fields(LF_COMMESSE, y, dlg, cms_req); } } } if (fasinfo.levels() > 0 && fasinfo.parent() <= 0) create_sheet_fields(LF_FASI, y, dlg, fsc_req); _use_pdc = ini.get_bool("UsePdcc"); const bool pdc_req = ini.get_bool("PdciRequired"); create_sheet_fields(_use_pdc ? LF_PCON : LF_PCONANA, y, dlg, pdc_req); for (short id = S_CON4+100; id >= S_CDC1+100; id--) { const int pos = sm.id2pos(id); if (pos >= 0) { TMask_field& f = sm.fld(pos); const int size = f.size(); const TString& prompt = f.prompt(); sf.set_column_header(id, prompt); sf.set_column_justify(id, f.is_kind_of(CLASS_REAL_FIELD)); sf.set_column_width(id, (max(3+size, prompt.len()+1)) * CHARX); } else { sf.delete_column(id); } } } TMovanal_msk::TMovanal_msk() : TAutomask("ca2100a") { create_sheet(); } /////////////////////////////////////////////////////////// // TMovanal_app /////////////////////////////////////////////////////////// TMask* TMovanal_app::get_mask(int) { return _msk; } TRelation* TMovanal_app::get_relation() const { return _rel; } bool TMovanal_app::get_next_key(TToken_string& key) { long num = 1; TLocalisamfile movana(LF_MOVANA); if (movana.last() == NOERR) num += movana.get_long(MOVANA_NUMREG); key.format("%d|%ld", F_NUMREG, num); return true; } void TMovanal_app::write_rows(const TMask& m) { TAnal_mov& mov = (TAnal_mov&)_rel->curr(); mov.destroy_rows(); // Scandisce lo sheet e riempie il recarray TSheet_field& sheet = m.sfield(F_RIGHE); FOR_EACH_SHEET_ROW(sheet, i, row) { const real dare = row->get(0), avere = row->get(); if (dare.is_zero() && avere.is_zero()) // Scarta righe vuote continue; TRectype& rec = mov.new_row(); // Crea una riga nuova _msk->row2rec(i, rec); } //FOR_EACH_SHEET... } void TMovanal_app::read_rows(const TMask& m) { const TAnal_mov& mov = (const TAnal_mov&)_rel->curr(); const TRecord_array& a = mov[LF_RMOVANA]; TSheet_field& sheet = m.sfield(F_RIGHE); sheet.destroy(); _msk->reset_saldi(); for (int i = 1; i <= a.rows(); i++) { const TRectype& rec = a.row(i); _msk->rec2row(rec, i-1); TImporto imp; _msk->row2imp(i-1, imp); TImporto& sld = _msk->saldo(rec); sld -= imp; } } bool TMovanal_app::protected_record(TRectype& rec) { return rec.get_bool(MOVANA_BLOCCATO); } int TMovanal_app::write(const TMask& m) { m.autosave(*_rel); write_rows(m); return _rel->write(); } int TMovanal_app::rewrite(const TMask& m) { m.autosave(*_rel); write_rows(m); return _rel->rewrite(); } int TMovanal_app::read(TMask& m) { const int err = TRelation_application::read(m); if (err == NOERR) read_rows(m); return err; } void TMovanal_app::init_query_mode(TMask& m) { ((TMovanal_msk&)m).show_locked_buttons(); } void TMovanal_app::init_insert_mode(TMask& m) { ((TMovanal_msk&)m).show_locked_buttons(); } void TMovanal_app::init_modify_mode(TMask& m) { ((TMovanal_msk&)m).show_locked_buttons(); } void TMovanal_app::ini2sheet(TConfig& ini, TSheet_field& sheet) { TString8 para; para.format("%d,1", LF_RMOVANA); if (ini.set_paragraph(para)) // Esiste la prima riga? { TRectype rec(LF_RMOVANA); // Crea una riga di appoggio sheet.destroy(); // Azzera lo sheet _msk->reset_saldi(); // Azzera i saldi for (int r = 1; ; r++) { para.format("%d,%d", LF_RMOVANA, r); if (!ini.set_paragraph(para)) break; // Riempie il record temporaneo coi dati del .ini rec.zero(); for (int i = 0; i < rec.items(); i++) { const char* field = rec.fieldname(i); const TString& str = ini.get(field); if (str.full()) rec.put(field, str); } _msk->rec2row(rec, r-1); // Riempie la riga dello sheet a partire da record // Aggiorna saldo in base all'importo della riga TImporto imp; _msk->row2imp(r-1, imp); TImporto& sld = _msk->saldo(rec); sld -= imp; } } } void TMovanal_app::sheet2ini(TSheet_field& sheet,TConfig& ini) { TRectype rec(LF_RMOVANA); // Crea una riga di appoggio TString8 para; FOR_EACH_SHEET_ROW(sheet, i, row) { rec.zero(); rec.put(RMOVANA_NUMREG, _msk->get(F_NUMREG)); rec.put(RMOVANA_NUMRIG, i+1); _msk->row2rec(i, rec); para.cut(0) << LF_RMOVANA << ',' << (i+1); ini.set_paragraph(para); for (int i = 0; i < rec.items(); i++) { const char* field = rec.fieldname(i); const TString& str = rec.get(field); if (str.full()) ini.set(field, str); } } } bool TMovanal_app::user_create() { _rel = new TRelation(LF_MOVANA); _rel->file().set_curr(new TAnal_mov); _msk = new TMovanal_msk; return true; } bool TMovanal_app::user_destroy() { delete _msk; _msk = NULL; delete _rel; _rel = NULL; return true; } int ca2100(int argc, char* argv[]) { TMovanal_app app; app.run(argc, argv, TR("Movimenti analitici")); return 0; }