campo-sirio/ca/ca2100.cpp

734 lines
20 KiB
C++
Raw Normal View History

#include <automask.h>
#include <defmask.h>
#include <recarray.h>
#include <recset.h>
#include <relapp.h>
#include <statbar.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 "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;
}