#include #include #include #include #include #include #include #include "../ca/calib01.h" #include "../ca/calib02.h" #include "../ca/commesse.h" #include "../ca/movana.h" #include "../ca/rmovana.h" #include "ps1001.h" #include "ps1001300a.h" /////////////////////////////////////////////////////////// // TAutomask /////////////////////////////////////////////////////////// class TVariazione_budget_mask : public TAutomask { bool _dirty; //posizioni dei vari campi dello sheet: vengono assegnati nel costruttore int _pos_check, _pos_cdc, _pos_fase, _pos_conto, _pos_datacomp, _pos_autofcomp, _pos_datafcomp, _pos_cosric, _pos_imp, _pos_prev, _pos_mat, _pos_descr, _pos_numreg, _pos_numrig, _pos_tipomov; protected: virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); int carica_rmovana(); bool one_checked() const; //(on_field) controlla se nella colonna dei check ce ne è almeno 1 checkato void check_all(const bool checked); //(on_field) checka-dechecka la colonna dei check int find_sister_rows(const int curr_sister, int& first_sister, int& last_sister); //(on_field) trova le righe con la stessa chiave della riga corrente void build_key(int i, TToken_string& key); //(find_sister_rows) crea la chiave della riga con cui cercare le sorelle void aggiorna_saldi_preventivi(TSheet_field& sf_righe, const int curr_riga); //(carica_rmovana()) crea i saldi preventivi in base agli importi void save_commessa(); //(save()) salva i cambiamenti di date della commessa esaminata void save_sheet(); //(save()) salva i cambiamenti nello sheet bool save(); //(on_field) gestisce l'ordine dei salvataggi public: TVariazione_budget_mask(); ~TVariazione_budget_mask(); }; TVariazione_budget_mask::TVariazione_budget_mask() :TAutomask ("ps1001300a") { //carica la causale che trova nel ditta.ini TConfig ditta_ini(CONFIG_DITTA, "ps1001"); set(F_CODCAUS, ditta_ini.get("CodCaus")); //setta le posizioni dei campi dello sheet TSheet_field& sf_righe = sfield(F_RIGHE); _pos_check = sf_righe.cid2index(S_CHECK); _pos_cdc = sf_righe.cid2index(S_CDC); _pos_fase = sf_righe.cid2index(S_FASE); _pos_conto = sf_righe.cid2index(S_CONTO); _pos_datacomp = sf_righe.cid2index(S_DATACOMP); _pos_autofcomp = sf_righe.cid2index(S_AUTOFCOMP); _pos_datafcomp = sf_righe.cid2index(S_DATAFCOMP); _pos_cosric = sf_righe.cid2index(S_COSRIC); _pos_imp = sf_righe.cid2index(S_IMPORTO); _pos_prev = sf_righe.cid2index(S_PREVENTIVO); _pos_mat = sf_righe.cid2index(S_MATURATO); _pos_descr = sf_righe.cid2index(S_DESCR); _pos_numreg = sf_righe.cid2index(S_NUMREG); _pos_numrig = sf_righe.cid2index(S_NUMRIG); _pos_tipomov = sf_righe.cid2index(S_TIPOMOV); } TVariazione_budget_mask::~TVariazione_budget_mask() { //salva nel ditta.ini la causale sulla maschera TConfig ditta_ini(CONFIG_DITTA, "ps1001"); ditta_ini.set("CodCaus", get(F_CODCAUS)); } void TVariazione_budget_mask::build_key(int i, TToken_string& key) { TSheet_field& sf_righe = sfield(F_RIGHE); TToken_string curr_riga = sf_righe.row(i); //chiave della riga sorella originale key.cut(0); key.add(curr_riga.get(_pos_cdc)); key.add(curr_riga.get(_pos_fase)); key.add(curr_riga.get(_pos_conto)); } int TVariazione_budget_mask::find_sister_rows(const int curr_sister, int& first_sister, int& last_sister) { first_sister = last_sister = curr_sister; TToken_string key; build_key(curr_sister, key); TToken_string key_iesima; for (int i = curr_sister - 1; i >= 0; i--) { build_key(i, key_iesima); if (key == key_iesima) first_sister = i; else break; } TSheet_field& sf_righe = sfield(F_RIGHE); const long items = sf_righe.items(); for (int i = curr_sister + 1; i < items; i++) { build_key(i, key_iesima); if (key == key_iesima) last_sister = i; else break; } return last_sister - first_sister + 1; } bool TVariazione_budget_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) { switch (o.dlg()) { //maschera case F_CODCMS: if (e == fe_modify) { bool e = carica_rmovana() == 0; o.enable(e); enable(F_DESCRIZ, e); } break; case F_DATAINICMS: case F_DATAFINECMS: _dirty = true; break; //sheet case S_CHECK: if (e == fe_modify) { //se viene checkata una riga deve uncheckare le sue sorelle di chiave TSheet_field& sf_righe = sfield(F_RIGHE); int curr = sf_righe.selected(), first, last; const int sisters = find_sister_rows(curr, first, last); for (int i = first; i <= last; i++) sf_righe.row(i).add(i == curr ? "X" : "", _pos_check); sf_righe.force_update(); } break; case S_DATACOMP: case S_DATAFCOMP: if (e == fe_modify) { const TDate datainicms = get_date(F_DATAINICMS); TDate datafinecms = get_date(F_DATAPRORCMS); if (!datafinecms.ok()) datafinecms = get_date(F_DATAFINECMS); const TDate data = o.get(); if (data < datainicms || (datafinecms.ok() && data > datafinecms)) return error_box("Le date competenza devono rientrare nella durata della commessa!"); _dirty = true; } break; case S_IMPORTO: if (e == fe_modify) { //se viene cambiato a mano un importo, deve aggiornare i saldi preventivi TSheet_field& sf_righe = sfield(F_RIGHE); const int curr_riga = sf_righe.selected(); sf_righe.row(curr_riga).add(o.get(), _pos_imp); aggiorna_saldi_preventivi(sf_righe, curr_riga); o.mask().set(S_PREVENTIVO, sf_righe.cell(curr_riga, _pos_prev)); sf_righe.force_update(); _dirty = true; } break; //bottoni case DLG_CANCEL: if (e == fe_button) { if (_dirty) { if (yesno_box("Salvare le modifiche effettuate?")) save(); } enable(F_CODCMS); enable(F_DESCRIZ); sfield(F_RIGHE).destroy(); _dirty = false; return false; } break; case DLG_CHECKALL: if (e == fe_button) { check_all(!one_checked()); } break; case DLG_TODATAINI: if (e == fe_button) { const TString& datainicms = get(F_DATAINICMS); TSheet_field& sf_righe = sfield(F_RIGHE); FOR_EACH_SHEET_ROW(sf_righe, i, riga) if (riga->get_char(0) > ' ') //solo le righe checked!! riga->add(datainicms, _pos_datacomp); sf_righe.force_update(); } break; case DLG_TODATAFIN: if (e == fe_button) { TString datafinecms = get(F_DATAPRORCMS); if (!datafinecms.ok()) datafinecms = get(F_DATAFINECMS); TSheet_field& sf_righe = sfield(F_RIGHE); FOR_EACH_SHEET_ROW(sf_righe, i, riga) if (riga->get_char(0) > ' ') //solo le righe checked!! riga->add(datafinecms, _pos_datafcomp); sf_righe.force_update(); } break; case DLG_TOMATURATO: if (e == fe_button) { TSheet_field& sf_righe = sfield(F_RIGHE); FOR_EACH_SHEET_ROW(sf_righe, i, riga) if (riga->get_char(0) > ' ') //solo le righe checked!! { //cerca le sorelle della riga int first, last; const int sisters = find_sister_rows(i, first, last); //calcola lo sbilancio tra i saldi maturati e preventivi: il suo scopo sarà quello di modificare gli importi in modo.. //..da azzerare tale sbilancio const real mat = real(riga->get(_pos_mat)); const real prev = real(riga->get(_pos_prev)); real sbilancio = mat - prev; //legge l'importo della riga checkata real imp = real(riga->get(_pos_imp)); //1) se la riga non ha sorelle -> modifica l'importo della riga stessa per azzerare lo sbilancio e stop! //2) se lo sbilancio è > 0 oppure se lo sbilancio è < 0 ma più piccolo dell'importo, basta agire sulla riga stessa.. //..per azzerare lo sbilancio e stop! if (sbilancio >= -imp || sisters == 1) { imp += sbilancio; sbilancio = ZERO; riga->add(imp.string(), _pos_imp); } //3) se invece ci sono sorelle oppure lo sbilancio è < 0 e più grande dell'importo della riga, allora vanno azzerate le.. //..righe sorelle partendo da quella checkata, poi passando all'ultima e risalendo fino a che lo sbilancio non si annulli.. //..Insomma una strage di righe che si azzerano else { riga->add("", _pos_imp); sbilancio += imp; for (int j = last; j >= first && !sbilancio.is_zero(); j--) { imp = sf_righe.cell(j, _pos_imp); if (j == first) { imp += sbilancio; sbilancio = ZERO; sf_righe.row(j).add(imp.string(), _pos_imp); } else { const real k = min(imp, -sbilancio); if (k > ZERO) { imp -= k; sbilancio += k; sf_righe.row(j).add(imp.string(), _pos_imp); } } } } //al termine deve aggiornare la colonna con i preventivi aggiorna_saldi_preventivi(sf_righe, i); } sf_righe.force_update(); _dirty = true; } break; case DLG_SAVEREC: if (e == fe_button && check_fields()) { const bool saved = save(); } break; default: break; } return true; } /////////////////////////////////// // Metodi della on_field_event /////////////////////////////////// //salva la commessa con le eventuali modifiche effettuate sulle date void TVariazione_budget_mask::save_commessa() { TLocalisamfile commesse(LF_COMMESSE); commesse.put(COMMESSE_CODCMS, get(F_CODCMS)); int err = commesse.read(_isequal, _lock); if (err == NOERR) { commesse.put(COMMESSE_DATAINIZIO, get_date(F_DATAINICMS)); commesse.put(COMMESSE_DATAFINE, get_date(F_DATAFINECMS)); commesse.put(COMMESSE_PROROGA, get_bool(F_PROROGATA)); commesse.put(COMMESSE_DATAPROR, get_date(F_DATAPRORCMS)); //solo la rewrite perchè la commessa ovviamente esiste già commesse.rewrite(); } } void TVariazione_budget_mask::save_sheet() { } bool TVariazione_budget_mask::save() { //salva nel ditta.ini la causale sulla maschera TConfig ditta_ini(CONFIG_DITTA, "ps1001"); ditta_ini.set("CodCaus", get(F_CODCAUS)); //salva le modifiche alle date della commessa save_commessa(); //salva lo sheet (è il programma principale) save_sheet(); _dirty = false; return true; } //controlla sulla colonna delle spunte se almeno una è checkata bool TVariazione_budget_mask::one_checked() const { TSheet_field& sf_righe = sfield(F_RIGHE); FOR_EACH_SHEET_ROW(sf_righe, i, riga) { if (riga->get_char(0) > ' ') return true; } return false; } //checka-dechecka la colonna di spunte dello sheet void TVariazione_budget_mask::check_all(const bool checked) { TSheet_field& sf_righe = sfield(F_RIGHE); FOR_EACH_SHEET_ROW(sf_righe, i, riga) { int first, last; find_sister_rows(i, first, last); for (int j = first; j <= last; j++) sf_righe.row(j).add(j == last ? "X" : "", _pos_check); i = last; } sf_righe.force_update(); } //aggiorna la colonna con i saldi preventivi sullo sheet void TVariazione_budget_mask::aggiorna_saldi_preventivi(TSheet_field& sf_righe, const int curr_riga) { int first, last; const int sisters = find_sister_rows(curr_riga, first, last); //calcola il totale degli importi delle righe sorelle in modo da avere il saldo real saldo_prev; for (int j = first; j <= last; j++) { TString80 str_imp = sf_righe.row(j).get(_pos_imp); saldo_prev += real(str_imp); } //mette il saldo in tutte le righe sorelle for (int j = first; j <= last; j++) { TToken_string& row = sf_righe.row(j); row.add(saldo_prev.string(), _pos_prev); } } //ordina per numreg/numrig (long+int) static int compare_by_numrig(TSheet_field & s, int r1, int r2) { TToken_string& s1 = s.row(r1); TToken_string& s2 = s.row(r2); //prima guarda il numreg.. long c10 = s1.get_long(12); long c20 = s2.get_long(12); int cmp = c10 - c20; //..poi il numrig if (cmp == 0) { int c11 = s1.get_int(13); int c21 = s2.get_int(13); cmp = c11 - c21; } return cmp; } //nota: nelle ststic non si possono usare i _pos_quel, perchè sono di maschera //ordina le righe per sede/fase/conto (string+string+string) static int compare_by_fase(TSheet_field & s, int r1, int r2) { TToken_string& s1 = s.row(r1); TToken_string& s2 = s.row(r2); TToken_string c1; c1.add(s1.get(1)); c1.add(s1.get(2)); c1.add(s1.get(3)); TToken_string c2; c2.add(s2.get(1)); c2.add(s2.get(2)); c2.add(s2.get(3)); int cmp = c1.compare(c2); if (cmp == 0) cmp = compare_by_numrig(s, r1, r2); return cmp; } int TVariazione_budget_mask::carica_rmovana() { TString query; query << "USE RMOVANA KEY 4\n"; query << "SELECT ((MOVANA.TIPOMOV=\"P\")||(MOVANA.TIPOMOV=\"V\"))\n"; query << "JOIN MOVANA INTO NUMREG==NUMREG\n"; query << "FROM CODCMS=#CODCMS\n"; query << "TO CODCMS=#CODCMS\n"; //instanzio un TISAM_recordset sulle rmovana TISAM_recordset rmovana(query); const TString cms = get(F_CODCMS); rmovana.set_var("#CODCMS", cms); const long rmovana_items = rmovana.items(); TProgind pi(rmovana_items, "Generazione righe in corso...", true, true); //recupero sheet e realtiva mashera di riga TSheet_field& sf_righe = sfield(F_RIGHE); TMask& msk = sf_righe.sheet_mask(); sf_righe.destroy(); //per ogni riga del recordset va ad aggiornare lo sheet sulla maschera (aggiunge la riga) for (bool ok = rmovana.move_first(); ok; ok = rmovana.move_next()) { if (!pi.addstatus(1)) break; TToken_string& row = sf_righe.row(-1); //chiave iniziale di riga const TString& cdc = rmovana.get(RMOVANA_CODCCOSTO).as_string(); row.add(cdc, _pos_cdc); const TString& fase = rmovana.get(RMOVANA_CODFASE).as_string(); row.add(fase, _pos_fase); const TString& conto = rmovana.get(RMOVANA_CODCONTO).as_string(); row.add(conto, _pos_conto); //date competenza iniziale e finale const TDate datacomp = rmovana.get("MOVANA."MOVANA_DATACOMP).as_date(); TDate datafcomp = rmovana.get("MOVANA."MOVANA_DATAFCOMP).as_date(); const bool autofcomp = rmovana.get("MOVANA."MOVANA_AUTOFCOMP).as_bool(); //caso cazzuto della scadenza con la fine della commessa (come nella stampa bilancio commessa) if (autofcomp) { const TRectype& rec_commesse = cache().get(LF_COMMESSE, cms); //data del cazzo che serve per non rovinare datacomp, che è la data sulla riga, non quella iniziale di cms TDate datainicms; ca_durata_commessa(rec_commesse, datainicms, datafcomp); } if (!datafcomp.ok()) datafcomp = datacomp; row.add(datacomp, _pos_datacomp); row.add(autofcomp ? "X" : "", _pos_autofcomp); row.add(datafcomp, _pos_datafcomp); //costo o ricavo? all'indbil l'ardua sentenza! TAnal_bill bill(conto, cdc, cms, fase); const int indbil = bill.indicatore_bilancio(); const char* str_indbil = indbil == 3 ? "C" : "R"; row.add(str_indbil, _pos_cosric); real valore = rmovana.get(RMOVANA_IMPORTO).as_real(); const char sezione = rmovana.get(RMOVANA_SEZIONE).as_string()[0]; TImporto importo(sezione, valore); importo.normalize(indbil == 3 ? 'D' : 'A'); valore = importo.valore(); row.add(valore.string(), _pos_imp); const TDate dataini, datafin; //recupera il saldo finale consuntivo! in base alla chiave cms/cdc/fase/conto e lo mette nel campo consuntivo const TSaldanal& saldanal_cons = ca_saldo(bill, dataini, datafin, _saldanal_consuntivo | _saldanal_ultima_imm); TImporto saldo_cons = saldanal_cons._fin; saldo_cons.normalize(indbil == 3 ? 'D' : 'A'); const real saldo_cons_valore = saldo_cons.valore(); row.add(saldo_cons_valore.string(), _pos_mat); //completa la riga row.add(rmovana.get(RMOVANA_DESCR).as_string(), _pos_descr); row.add(rmovana.get(RMOVANA_NUMREG).as_int(), _pos_numreg); row.add(rmovana.get(RMOVANA_NUMRIG).as_int(), _pos_numrig); const TString& tipomov = rmovana.get("MOVANA."MOVANA_TIPOMOV).as_string(); row.add(tipomov, _pos_tipomov); } //for(bool ok.move.... //prima di riempire lo sheet a video ordina le righe per sede/fase/numreg/numrig sf_righe.sort(compare_by_fase); //in base alle righe caricate ricava i saldi preventivi FOR_EACH_SHEET_ROW(sf_righe, i, riga) { //solo le righe a saldo preventivo nullo vanno considerate const TString& str_saldo_prev = riga->get(_pos_prev); if (str_saldo_prev == "") { aggiorna_saldi_preventivi(sf_righe, i); } } //e poi aggiorna il video! sf_righe.force_update(); return sf_righe.items(); } /////////////////////////////////////// // TSkeleton_application /////////////////////////////////////// class TVariazione_budget : public TSkeleton_application { virtual bool check_autorization() const { return false; } virtual const char * extra_modules() const { return "ca"; } TVariazione_budget_mask* _msk; protected: public: virtual bool create(); virtual bool destroy(); virtual void main_loop(); TVariazione_budget() {}; }; bool TVariazione_budget::create() { _msk = new TVariazione_budget_mask(); return TSkeleton_application::create(); } bool TVariazione_budget::destroy() { delete _msk; return TApplication::destroy(); } void TVariazione_budget::main_loop() { KEY tasto; tasto = _msk->run(); } int ps1001300 (int argc, char* argv[]) { TVariazione_budget main_app; main_app.run(argc, argv, TR("Variazione budget")); return true; }