#include #include #include #include #include #include #include "ca2.h" #include "ca2100a.h" #include "calib01.h" #include "../cg/cglib01.h" #include #include #include "movana.h" #include "rmovana.h" #include "saldana.h" class TMovanal_msk : public TAutomask { TAssoc_array _saldi; bool _use_pdc; 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); const TString& somma_campi(TToken_string& row, int first, bool pdc = false) const; // Lettura movimento contabile TToken_string& get_rip_row(const TRectype& rrip); void split_cg_row(const TRectype& row, const TRecord_array& rrip); void load_cg_row(const TRectype& row); void load_cg_mov(); 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; void spezza_campo(const TString& str, TToken_string& row, int first) const; TMovanal_msk(); virtual ~TMovanal_msk() { } }; class TMovanal_app : public TRelation_application { TRelation* _rel; TMovanal_msk* _msk; protected: 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 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 { return mode() != MODE_MOD || !get_bool(F_BLOCCATO); } bool TMovanal_msk::on_key(KEY key) { if (key == K_SHIFT + K_F12 && !can_be_closed()) { enable(F_BLOCCATO); return true; } return TAutomask::on_key(key); } void TMovanal_msk::show_locked_buttons() { const bool editmode = mode() == MODE_MOD; const bool unlocked = can_be_closed(); const bool is_trans = app().is_transaction(); show (F_BLOCCATO, editmode); enable(F_BLOCCATO, unlocked); enable(DLG_SAVEREC, unlocked && (editmode || mode() == MODE_INS)); enable(DLG_DELREC, unlocked && editmode); enable(DLG_QUIT, unlocked && !is_trans); enable(DLG_CANCEL, mode() == MODE_QUERY || !is_trans); enable(F_RESET, unlocked); } TToken_string& TMovanal_msk::get_rip_row(const TRectype& rrip) { TSheet_field& sheet = sfield(F_RIGHE); const int pos_cdc = sheet.cid2index(S_CDC1); const int pos_cms = sheet.cid2index(S_CMS1); const int pos_fas = sheet.cid2index(S_FAS1); const int pos_con = sheet.cid2index(S_CON1); TToken_string keyrip, keyrow; keyrip.add(rrip.get("CODCOSTO")); keyrip.add(rrip.get("CODCMS")); keyrip.add(rrip.get("CODFASE")); keyrip.add(rrip.get("CODCONTO")); if (sheet.items() > 0) { FOR_EACH_SHEET_ROW(sheet, i, row) { keyrow = somma_campi(*row, pos_cdc); keyrow.add(somma_campi(*row, pos_cms)); keyrow.add(somma_campi(*row, pos_fas)); keyrow.add(somma_campi(*row, pos_con, _use_pdc)); if (keyrow == keyrip) return *row; } } TToken_string& row = sheet.row(-1); spezza_campo(keyrip.get(0), row, pos_cdc); spezza_campo(keyrip.get(1), row, pos_cms); spezza_campo(keyrip.get(2), row, pos_fas); spezza_campo(keyrip.get(3), row, pos_con); return row; } void TMovanal_msk::split_cg_row(const TRectype& row, const TRecord_array& 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++) distrib.add(rrip[i].get_real("RIPARTO")); 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 TString80 desc; riga.get(2); // 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 } } void TMovanal_msk::load_cg_row(const TRectype& row) { // Cerco la ripartizione del sottoconto, se non la trovo uso quella del conto o del gruppo for (int i = 3; i > 0; i--) { TString query = "USE RIP SELECT"; query << " (GRUPPO=" << row.get(RMV_GRUPPO) << ')'; // Il gruppo c'e' sempre if (i > 1) query << "&&(CONTO=" << row.get(RMV_CONTO) << ')'; // Il conto c'e' per i = 2 o 3 if (i > 2) query << "&&(SOTTOCONTO=" << row.get(RMV_SOTTOCONTO) << ')'; // Il sottoconto c'e' per i = 3 query << "\nFROM TIPO='I'\nTO TIPO='I'"; TISAM_recordset rs(query); if (rs.items() > 0) // Ho trovato la ripartizione: evviva! { TString16 codice = rs.get("CODICE").as_string(); codice.insert("I|"); TRecord_array rrip(codice, LF_RRIP); // Carico le righe di ripartizione if (rrip.rows() > 0) { split_cg_row(row, rrip); break; } } } if (i == 0) // Non ho trovato nessuno schema di riaprtizione valido { // Creo una riga nuova TToken_string& riga = sfield(F_RIGHE).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 } statbar_set_title(TASK_WIN, NULL); // Restore mask mode deleted by query stats } // 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)); TImporto totdoc; TRecord_array cg(numregcg, LF_RMOV); const bool autoinsert = sfield(F_RIGHE).items() == 0; 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); } bool TMovanal_msk::on_field_event(TOperable_field& o, TField_event e, long jolly) { 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; if (datacomp > datareg) return error_box(TR("La data di competenza non puo' superare la data di registrazione")); const TEsercizi_contabili ec; const int ae = ec.date2esc(datacomp); 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); 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_RIGHE: if (e == se_enter) { const int r = int(jolly); aggiorna_saldo_riga(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; } 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) { TSheet_field& sf = sfield(F_RIGHE); sf.destroy(); load_cg_mov(); sf.force_update(); } 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; } const TString& TMovanal_msk::somma_campi(TToken_string& row, int first, bool pdc) const { TSheet_field& sheet = sfield(F_RIGHE); TMask& m = sheet.sheet_mask(); const short id = S_DARE + 100 + first; TString& str = get_tmp_string(20); TString80 token; for (int i = 0; i < 4; i++) { if (m.id2pos(id+i) < 0) break; const TEdit_field& fld = m.efield(id+i); token = row.get(first+i); if (pdc) token.right_just(fld.size(), '0'); else token.left_just(fld.size()); str << token; } return str; } void TMovanal_msk::spezza_campo(const TString& str, TToken_string& row, int first) const { TSheet_field& sheet = sfield(F_RIGHE); TMask& m = sheet.sheet_mask(); TString80 token; const short id = 201 + first; int start = 0; for (int i = 0; i < 4; i++) { if (m.id2pos(id+i) < 0) break; const TEdit_field& fld = m.efield(id+i); const int len = fld.size(); token = str.mid(start, len); token.trim(); row.add(token, first+i); start += len; } } // 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; TToken_string& row = sheet.row(r); rec.put(RMOVANA_ANNOES, get(F_ANNOES)); rec.put(RMOVANA_DATAREG, get(F_DATAREG)); rec.put(RMOVANA_DESCR, row.get(sheet.cid2index(S_DESCR))); const real dare = row.get(sheet.cid2index(S_DARE)), 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); } rec.put(RMOVANA_CODCCOSTO, somma_campi(row, sheet.cid2index(S_CDC1))); rec.put(RMOVANA_CODCMS, somma_campi(row, sheet.cid2index(S_CMS1))); rec.put(RMOVANA_CODFASE, somma_campi(row, sheet.cid2index(S_FAS1))); rec.put(RMOVANA_CODCONTO, somma_campi(row, sheet.cid2index(S_CON1), _use_pdc)); 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); } 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); } TMovanal_msk::TMovanal_msk() : TAutomask("ca2100a") { TSheet_field& sf = sfield(F_RIGHE); TMask& sm = sf.sheet_mask(); sm.hide(-1); // Nasconde tutti campi fittizi TConfig ini(CONFIG_DITTA, "ca"); _use_pdc = ini.get_bool("UsePdcc"); const short id_cdc = 201+sf.cid2index(S_CDC1); const short id_cms = 201+sf.cid2index(S_CMS1); const short id_fas = 201+sf.cid2index(S_FAS1); const short id_con = 201+sf.cid2index(S_CON1); ca_create_fields(sm, LF_CDC, 1, 2, id_cdc, id_cdc+50); ca_create_fields(sm, LF_COMMESSE, 1, 6, id_cms, id_cms+50); ca_create_fields(sm, LF_FASI, 1, 10, id_fas, id_fas+50); ca_create_fields(sm, _use_pdc ? LF_PCON : LF_PCONANA, 1, 14, id_con, id_con+50); for (short id = id_con+3; id >= id_cdc; 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); switch (100+(f.dlg()%100)) { case S_CDC1: case S_CDC2: case S_CDC3: case S_CDC4: f.check_type(ini.get_bool("CdcRequired") ? CHECK_REQUIRED : CHECK_NORMAL); break; case S_CMS1: case S_CMS2: case S_CMS3: case S_CMS4: f.check_type(ini.get_bool("CmsRequired") ? CHECK_REQUIRED : CHECK_NORMAL); break; case S_FAS1: case S_FAS2: case S_FAS3: case S_FAS4: f.check_type(ini.get_bool("FscRequired") ? CHECK_REQUIRED : CHECK_NORMAL); break; case S_CON1: case S_CON2: case S_CON3: case S_CON4: f.check_type(ini.get_bool("PdciRequired") ? CHECK_REQUIRED : CHECK_NORMAL); break; default: break; } } else { sf.delete_column(id); } } } /////////////////////////////////////////////////////////// // 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 = _msk->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); } } 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); TToken_string& row = sheet.row(i-1); const TImporto imp(rec.get_char(RMOVANA_SEZIONE), rec.get_real(RMOVANA_IMPORTO)); _msk->imp2row(imp, row); row.add(rec.get(RMOVANA_DESCR)); // Calcola le posizioni dei campi multilivello generati const int pos_cdc = sheet.cid2index(S_CDC1); const int pos_cms = sheet.cid2index(S_CMS1); const int pos_fas = sheet.cid2index(S_FAS1); const int pos_con = sheet.cid2index(S_CON1); _msk->spezza_campo(rec.get(RMOVANA_CODCCOSTO), row, pos_cdc); _msk->spezza_campo(rec.get(RMOVANA_CODCMS), row, pos_cms); _msk->spezza_campo(rec.get(RMOVANA_CODFASE), row, pos_fas); _msk->spezza_campo(rec.get(RMOVANA_CODCONTO), row, pos_con); 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) { write_rows(m); return TRelation_application::write(m); } int TMovanal_app::rewrite(const TMask& m) { write_rows(m); return TRelation_application::rewrite(m); } 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(); } bool TMovanal_app::user_create() { _rel = new TRelation(LF_MOVANA); _rel->lfile().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, "Movimenti analitici"); return 0; }