campo-sirio/ca/ca2100.cpp
guy 9eacf264e0 Reso possibile utilizzo programmi di "ca" anche ai possessori di "ci"
git-svn-id: svn://10.65.10.50/branches/R_10_00@22916 c028cbd2-c16b-5b4b-a496-9718f37d4682
2014-03-07 13:59:08 +00:00

1288 lines
36 KiB
C++
Executable File
Raw Blame History

#include <defmask.h>
#include <execp.h>
#include <modaut.h>
#include <postman.h>
#include <recset.h>
#include "ca2.h"
#include "ca2100a.h"
#include "calib01.h"
#include "../cg/cglib01.h"
#include <causali.h>
#include <mov.h>
#include <rmov.h>
#include "cdc.h"
#include "commesse.h"
#include "fasi.h"
#include "movana.h"
#include "rmovana.h"
#include "rrip.h"
#include "saldana.h"
class TMovanal_msk : public TAutomask
{
TAssoc_array _saldi;
bool _use_pdc;
short _cms_start;
short _cms_end;
short _cdc_start;
short _cdc_end;
short _pcon_start;
short _pcon_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 completa_conto(int r);
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 "ci|cm"; } //deve funzionare anche per le commesse e l'industriale
virtual bool user_create();
virtual bool user_destroy();
virtual TMask* get_mask(int);
virtual bool get_next_key(TToken_string& key);
bool link_ci(const TMask& m);
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);
public:
virtual TRelation* get_relation() const;
};
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 TRelation* rel = app().get_relation();
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)) && user_can_write(rel);
show (F_BLOCCATO, modifying);
enable(F_BLOCCATO, unlocked);
enable(DLG_SAVEREC, unlocked && editing);
enable(DLG_DELREC, unlocked && !inserting && user_can_delete(rel));
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)
{
const bool ci = app().has_module(CIAUT);
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));
if (ci)
keyrip.add(rrip.get(RRIP_CODART));
TRectype rmov(LF_RMOVANA);
TSheet_field& sheet = sfield(F_RIGHE);
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 (ci)
keyrow.add(rmov.get(RMOVANA_CODART));
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());
if (ci)
rmov.put(RMOVANA_CODART, 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
for (int 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 (int 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<70> 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<70> 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 <20> 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<70> 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<63>..
//..is_ripartible_movcg sar<61> true (se ho una multiripartizione allora <20> 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 <20> 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<70> adatta all'utonto
if (rrip.has_multirip())
splitted = multi_split_cg_row(row, rrip);
else //se invece c'<27> 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<70> 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_notify_modify)
{
TSheet_field & s = sfield(F_RIGHE);
TMask & m = s.sheet_mask();
const int r = int(jolly);
completa_conto(r);
s.update_mask(r);
const bool pdc_req = ca_config().get_bool("PdciRequired");
if (pdc_req)
{
for (short id = _pcon_start; id < _pcon_end; id++)
{
TEdit_field & e = m.efield(id);
e.check_type(CHECK_REQUIRED);
e.set_dirty();
const bool ok = e.check();
e.check_type(CHECK_NORMAL);
if (!ok)
return error_box("Manca il conto analitico");
}
}
}
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)
{
if (yesno_box(FR("Il totale delle righe e' %s %s, mentre il totale documento e' %s %s.\n"
"Si desidera allineare il totale di testata a quello delle righe?"),
totrig.valore().string(), totrig.sezione() == 'D' ? TR("Dare") : TR("Avere"),
totdoc.valore().string(), totdoc.sezione() == 'D' ? TR("Dare") : TR("Avere")))
{
set(F_SEZIONE, totrig.sezione());
set(F_TOTDOC, totrig.valore());
}
else
return false;
}
}
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::completa_conto(int r)
{
TRectype rec(LF_RMOVANA);
row2rec(r, rec);
TString conto = rec.get(RMOVANA_CODCONTO);
if (conto.blank())
{
TToken_string key;
const TString4 livsup = ca_config().get("FathFasi");
if (livsup.blank())
key.add("");
else
if (livsup == "CDC")
key.add(rec.get(RMOVANA_CODCCOSTO));
else
key.add(rec.get(RMOVANA_CODCMS));
key.add(rec.get(RMOVANA_CODFASE));
conto = cache().get(LF_FASI, key, FASI_CODCONTO);
if (conto.blank())
{
for (int i = 2; conto.blank()&& i > 0; i--)
{
const TString4 level = ca_config().get("Level", NULL, i);
if (level == "CMS")
conto = cache().get(LF_COMMESSE, rec.get(RMOVANA_CODCMS), COMMESSE_CODCONTO);
else
if (level == "CDC")
conto = cache().get(LF_CDC, rec.get(RMOVANA_CODCCOSTO), CDC_CODCONTO);
}
}
if (conto.full())
{
rec.put(RMOVANA_CODCONTO, conto);
rec2row(rec, r);
}
}
}
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 = 4;
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");
_pcon_start = dlg;
create_sheet_fields(_use_pdc ? LF_PCON : LF_PCONANA, y, dlg, false);
_pcon_end = dlg;
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);
}
}
if (!main_app().has_module(CIAUT))
{
sm.hide(S_CODART);
sm.hide(S_DESCART);
sf.delete_column(S_CODART);
sf.delete_column(S_DESCART);
}
}
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...
}
bool TMovanal_app::link_ci(const TMask& m)
{
bool done = has_module(CIAUT);
if (done)
{
const TRectype& mov = _rel->curr();
const TRectype& caus = cache().get(LF_CAUSALI, mov.get(MOVANA_CODCAUS));
done = caus.exist(CAU_MOVCGIND) && caus.get_bool(CAU_MOVCGIND);
if (done)
{
TFilename n; n.tempdir(); n.add("mov_caci.ini");
n.fremove();
if (n.full()) // dummy test
{
m.field(F_NUMREG).set(mov.get(MOVANA_NUMREG));
TConfig cini(n, "107");
mask2ini(m, cini);
}
TString cmd; cmd << "ci0 -6 -i" << n;
TExternal_app ci0(cmd);
ci0.run();
}
}
return done;
}
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);
const int err = _rel->write();
if (err == NOERR)
link_ci(m);
return err;
}
int TMovanal_app::rewrite(const TMask& m)
{
m.autosave(*_rel);
write_rows(m);
const int err = _rel->rewrite();
if (err == NOERR)
link_ci(m);
return err;
}
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;
}