9863fed64d
Files correlati :ca2.exe Ricompilazione Demo : [ ] Commento : Sostituita error_box con yesno_box in caso di discordanza tra totale righe e totale testata nei movimenti analitici. Adesso chiede se si vuole allineare il totale testata a quello righe. git-svn-id: svn://10.65.10.50/trunk@20219 c028cbd2-c16b-5b4b-a496-9718f37d4682
1162 lines
33 KiB
C++
Executable File
1162 lines
33 KiB
C++
Executable File
#include <defmask.h>
|
||
#include <recset.h>
|
||
|
||
#include "ca2.h"
|
||
#include "ca2100a.h"
|
||
#include "calib01.h"
|
||
|
||
#include "../cg/cglib01.h"
|
||
|
||
#include <mov.h>
|
||
#include <rmov.h>
|
||
|
||
#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<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_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::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;
|
||
}
|