campo-sirio/cg/pagament.cpp

1544 lines
45 KiB
C++
Raw Normal View History

#include <applicat.h>
#include <utility.h>
#include "pagament.h"
#include <mov.h>
#include <partite.h>
#include <scadenze.h>
#include <pagsca.h>
inline void swap(int& x, int& y) {int tmp = x; x = y; y = tmp; }
int TPagamento::_rata_ifield(int n, int f) const
{
TToken_string& t = (TToken_string&)_rate[n];
return t.get_int(f);
}
long TPagamento::_rata_lfield(int n, int f) const
{
TToken_string& t = (TToken_string&)_rate[n];
return t.get_long(f);
}
real TPagamento::_rata_rfield(int n, int f) const
{
TToken_string& t = (TToken_string&)_rate[n];
return real(t.get(f));
}
TDate TPagamento::_rata_dfield(int n, int f) const
{
TToken_string& t = (TToken_string&)_rate[n];
return TDate(t.get(f));
}
const char* TPagamento::_rata_sfield(int n, int f) const
{
static char kak[16];
TToken_string& t = (TToken_string&)_rate[n];
const char* k = t.get(f);
if (k != NULL)
strncpy(kak, k, 16);
else
kak[0] = '\0';
return kak;
}
void TPagamento::set_inizio(const TDate& d)
{
_inizio = d;
// aggiusta _inizio secondo INSCAD; vedi mese commerciale etc.
if (_inscad == 'M')
{
if (_mcomm) _inizio.set_day(_inizio.month() == 2 ? 28 : 30);
else _inizio.set_end_month();
}
else
{
if (_inscad == 'F' && _mcomm && _inizio.day() == 31)
_inizio.set_day(30);
}
TDate data(d); // Aggiusta data iniziale con i gironi prima rata
next_scad(data, scad_rata(0), _mcomm, 0);
bool dummy;
recalc_rate(0, FALSE, NULL, data.string(), NULL, NULL, _rdiff, _mcomm, dummy);
}
void TPagamento::set_intervallo_rate(int in)
{
if (_mcomm && (in % 30) != 0)
{
if (yesno_box("E' specificato \"mese commerciale\". Si desidera annullarlo?"))
_mcomm = FALSE;
else
return;
}
for (int i = 1; i < n_rate(); i++)
{
TToken_string& ts = rata(i);
ts.add(in, 0);
}
_int_rate = in;
_dirty = TRUE;
}
void TPagamento::set_mese_commerciale(bool v, int& sscad)
{
_dirty = FALSE;
if (_mcomm == v) return;
if (sscad < 0) sscad = 30;
if ((sscad % 30) != 0) sscad = 30 * ((sscad/30)+1);
set_intervallo_rate(sscad);
_mcomm = v;
}
void TPagamento::set_rate_differenziate(int v)
{
_dirty = FALSE;
if (_rdiff ^ v == 2) return;
if (v == 2 && (100 % n_rate()) == 0)
{
int p = 100 / n_rate();
for (int i = _tpr < 4 ? 0 : 1; i < n_rate(); i++)
{
TToken_string& tt = rata(i);
tt.add(p,1);
}
}
_rdiff = (v != 2);
_dirty = TRUE;
}
void TPagamento::set_tipo_prima_rata(int v, int sscad)
{
_dirty = FALSE;
if (_tpr == v) return;
if (v < 4 && _tpr > 3)
{
for (int i = n_rate() - 1; i > 0; i--)
{
TToken_string& tt = rata(i);
tt.add(scad_rata(i-1),0);
tt.add(tipo_rata(i-1),2);
tt.add(ulc_rata(i-1),5);
}
_rate.destroy(0);
_rate.pack();
}
else
if ( _tpr < 4 && v > 3)
{
TToken_string* ttn = new TToken_string(32);
ttn->add(0, 0);
ttn->add(0, 1);
ttn->add(1, 2);
ttn->add("", 3);
ttn->add("", 4);
ttn->add("", 5);
_rate.insert(ttn,0);
for (int i = 0; i < (n_rate()-1); i++)
{
TToken_string& tt = rata(i);
tt.add(scad_rata(i+1),0);
tt.add(tipo_rata(i+1),2);
tt.add(ulc_rata(i+1), 5);
}
if (n_rate() == 2 && scad_rata(1) == 0)
{
// l'unica rata aveva scadenza 0; ci mettiamo n.giorni default
TToken_string& tt = rata(1);
tt.add(_int_rate, 0);
}
}
_tpr = v;
_dirty = TRUE;
}
void TPagamento::set_numero_rate(int n, int sscad, int rdiff)
{
_dirty = FALSE;
if (n == 0 || n == n_rate()) return;
const int nr = n_rate();
const int first = _tpr < 4 ? 0 : 1;
real sum = 0.0;
int i = 0;
// diciamo che gli diciamo il numero giusto
if (first == 1 && n < 2) return;
const int nact = first == 1 ? n - 1 : n;
real p = real(100) / real(nact);
p.round(2);
if (_inited || (!_inited && rdiff == 1)) // se e' inited rdiff e' 2 per forza
{
real tot = _inited ? (_tpr < 4 ? _firstr : _secndr) : real(100.0);
if (nact == 1)
{
if (_inited) set_imprata(first, tot);
set_percrata(first, real(100.0));
if (_inited && _tpr > 0 && _tpr < 4)
set_imprata(first, tpay_rata(first) + _secndr);
i = first + 1; // per rimozione successiva
}
else
{
if (nr == first + 1)
{
// suddividi equamente e riaggiusta la percentuale di conseguenza
real div = tot / real(nact);
real perc = real(100.0)*(div/tot);
for (i = first; i < n; i++)
{
if (i > first)
add_rata(perc, (sscad == -1 ? scad_rata(0) : sscad), tipo_rata(0));
if (_inited) set_imprata (i, div);
set_percrata(i, perc);
}
if (_inited && _tpr > 0 && _tpr < 4)
set_imprata(0, tpay_rata(0) + _secndr);
}
else if (nr > first + 1)
{
// dalla prima nota e' abilitato solo se rdiff == 2
// e' selezionato UGUALI: devo: lasciare la prima
// rata com'e' (a meno che non ce ne sia una sola) e
// suddividere il resto dell'importo tra le altre
real rfirst = _inited ? tpay_rata(0) : perc_rata(0);
if (_inited && _tpr > 0 && _tpr < 4) rfirst -= _secndr;
real rest = tot - _tpr >= 4 ? ZERO : rfirst;
const real div = rest / real(nact);
real perc = real(100.0)*(div/tot);
for (i = 1; i < n; i++)
{
if (i >= nr)
add_rata(perc, (sscad == -1 ? (i < nr ? scad_rata(i) : scad_rata(nr-1)) : sscad),
tipo_rata(nr-1), ulc_rata(nr-1));
if (_inited) set_imprata (i, div);
set_percrata(i, perc);
}
}
}
}
else
{
for (i = first; sum < real(100.0); i++)
{
if ((real(100.0) - sum) < p)
p = real(100.0) - sum;
sum += p;
// if necessary add remainder on first one
if ((real(100.0) - sum) /* still */ < p)
{
real prc = perc_rata(first);
prc += real(100.0) - sum;
TToken_string& rt = rata(first);
rt.add(prc.string(),1),
sum = 100;
}
set_rata(i, real(p),
i == 0 ? (i < nr ? scad_rata(0) : 0):
(sscad == -1 ? (i < nr ? scad_rata(i) : scad_rata(nr-1)) : sscad),
(i < nr ? tipo_rata(i) : tipo_rata(nr-1)));
}
}
// erase remaining
for (; i < nr; i++)
_rate.destroy(i);
_rate.pack();
_dirty = TRUE;
}
void TPagamento::set_cambio(const real& cambio)
{
if (cambio.sign() <= 0)
_cambio = 1.0;
else
_cambio = cambio;
set_round(_cambio == 1.0 ? 0 : 3);
const bool in_valuta = _cambio != 1.0;
real lit;
for (int i = _rate.items()-1; i >= 0; i--)
{
TToken_string& row = (TToken_string&)_rate[i];
if (in_valuta)
{
lit = tpay_rata(i) * _cambio;
row.add(lit.string(), 7);
}
else
row.add("", 7);
}
}
void TPagamento::next_scad(TDate& d, int scad, bool mcomm, int rata)
{
if (mcomm)
{
int nm = scad / 30;
int ny = nm / 12;
nm %= 12;
int newm = d.month() + nm;
if (newm > 12) { newm -= 12; ny++; }
bool last = d.is_end_month() && inizio_scadenza() == 'M';
int dy = d.day();
// la palla del febbraio & c.
if (rata > 1)
{
TDate oldd(data_rata(rata-2));
if (oldd.day() > dy) dy = oldd.day();
}
d.set_day(1); // il giorno 1 ce l'hanno tutti
d.set_month(newm);
d.set_year(d.year()+ny);
d.set_end_month();
if (!last && dy < d.day())
d.set_day(dy);
}
else d += scad;
}
void TPagamento::set_default_type(int type, bool change_existing)
{
_def_tpr = type;
if (change_existing)
{
for (int i = 0; i < n_rate(); i++)
{
TToken_string& tt = rata(i);
tt.add(type, 2);
}
_dirty = TRUE;
}
}
void TPagamento::set_default_ulc(const char* ulc, bool change_existing)
{
_def_ulc = ulc;
if (change_existing)
{
for (int i = 0; i < n_rate(); i++)
{
TToken_string& tt = rata(i);
tt.add(ulc, 5);
}
_dirty = TRUE;
}
}
void TPagamento::remove_rata(int i)
{
// non fa nessun ricalcolo, si limita ad impacchettare se
// necessario
_rate.destroy(i);
_rate.pack();
_dirty = TRUE;
}
TToken_string& TPagamento::add_rata(real perc, int day, int type, const char* ulc)
{
TToken_string* tt = new TToken_string(64);
tt->add(day); // scadenza
tt->add(perc.string()); // percentuale
tt->add(type); // tipo
tt->add(""); // data
tt->add(""); // importo
tt->add(ulc); // ulc
_rate.add(tt);
_dirty = TRUE;
return *tt;
}
TToken_string& TPagamento::set_rata (int index, real perc, int day, int type,
const char* ulc, const char* imp, const char* data)
{
TToken_string* tt = (TToken_string*)_rate.objptr(index);
const bool nwr = (tt == NULL);
if (nwr) tt = new TToken_string(64);
tt->add(day,0); // scadenza
tt->add(perc.string(),1); // percentuale
tt->add(type,2); // tipo
tt->add(data == NULL ? "" : data, 3);
tt->add(imp == NULL ? "" : imp, 4);
tt->add(ulc == NULL ? "" : ulc, 5);
if (imp && *imp > ' ' && _cambio != 1.0)
{
real implit(imp);
implit *= _cambio;
implit.round();
tt->add(implit.string(), 7);
}
if (!nwr)
{
if (index > _rate.items())
{
error_box("Rate non contigue");
delete tt;
}
}
else
{
_rate.add(tt,index);
_dirty = TRUE;
}
return *tt;
}
void TPagamento::set_imprata(int i, const real& r)
{
TToken_string& tt = (TToken_string&)_rate[i];
TDate d = _inizio;
for (int n = 0; n <= i; n++)
next_scad(d, scad_rata(n), _mcomm, n);
tt.add((const char*)d, 3);
tt.add(r.string(), 4);
if (_cambio != 1.0)
{
const real lit = r * _cambio;
tt.add(lit.string(), 7);
}
else
tt.add("", 7);
}
void TPagamento::set_percrata(int i, real r)
{
TToken_string& tt = (TToken_string&)_rate[i];
tt.add(r.string(), 1);
}
real TPagamento::recalc_percrata(int i)
{
real hm(_tpr < 4 ? _firstr : _secndr);
if (i == 0 && _tpr > 0 && _tpr < 4)
hm -= _secndr;
real perc = tpay_rata(i) * 100.0 / hm;
perc.round(2);
set_percrata(i, perc);
return perc;
}
TToken_string& TPagamento::set_rata(int index, const real& howmuch, const real& quanto,
const TDate& date, int type,const char* ulc, bool pagato)
{
// calcola percentuali e scadenze a partire dagli importi
TToken_string* tt = (TToken_string*)_rate.objptr(index);
const int first = _tpr < 4 ? 0 : 1;
const bool nwr = (tt == NULL); // nuova rata
if (nwr) tt = new TToken_string(64);
const TDate oldd = index > 0 ? data_rata(index -1) : _inizio;
const long day = date - oldd;
const real toshare(_tpr < 4 ? _firstr : _secndr);
real hm = howmuch;
if (index == first && _tpr > 0 && _tpr < 4)
hm -= _secndr;
real perc = (_tpr > 3 && index == 0) ? ZERO : hm/toshare;
perc *= real(100.0);
perc.round(2);
tt->cut(0);
tt->add(day); // 0 - scadenza
tt->add(perc.string()); // 1 - percentuale
tt->add(type); // 2 - tipo
tt->add(date.string()); // 3 - data
tt->add(howmuch.string()); // 4 - importo
tt->add(ulc); // 5 - ulc(era?)
tt->add(pagato ? "X" : " "); // 6 - pagata
tt->add(quanto.string()); // 7 - Importo in lire
if (!nwr)
{
if (index > _rate.items())
{
error_box("Rate non contigue");
delete tt;
}
}
else
{
_rate.add(tt, index);
_dirty = TRUE;
}
return *tt;
}
word TPagamento::validate() const
{
word res = 0x0000;
real r(0.0);
int first = _tpr < 4 ? 0 : 1;
real toshare(_tpr < 4 ? _firstr : _secndr);
// check percentages & prepare slicer
for (int i = first; i < n_rate(); i++)
r += perc_rata(i);
r.round(1);
if (r != real(100.0))
res |= P_RSUM;
if (_inited)
{
real tot;
for (int i = 0; i < n_rate(); i++)
tot += tpay_rata(i);
if (tot != _firstr+_secndr)
res |= P_TOTNC;
// check errori date scadenze (se istanziate)
TDate d(data_rata(0));
if (d < _inizio)
res |= P_INIZIO;
for (i = 1; i < n_rate(); i++)
{
if (data_rata(i) <= d)
{ res |= P_SCAD; break; }
d = data_rata(i);
}
}
return res;
}
void TPagamento::strerr(word err, TString& s)
{
s = "Errore:\n";
if (err & P_RSUM)
s << "Le percentuali non sommano a 100\n";
if (err & P_IMPNC)
s << "Le percentuali sono inconsistenti con gli importi\n";
if (err & P_SCAD)
s << "Le scadenze non sono consecutive\n";
if (err & P_INIZIO)
{
s << "La prima rata (" << data_rata(0) << ") e' antecedente";
s << " alla data del documento (" << _inizio << ")\n";
}
if (err & P_NEG)
s << "L'importo dato e' inferiore al minimo possibile\n";
if (err & P_TROP)
s << "L'importo dato e' superiore al massimo possibile\n";
if (err & P_TOTNC)
{
const real tot = _firstr + _secndr;
real imp;
for (int i = 0; i < n_rate(); i++)
imp += tpay_rata(i);
s << "La somma degli importi (" << imp.string(".");
s << ") e' diversa dal totale del pagamento (" << tot.string(".") << ")\n";
}
if (err & P_MCOMM)
s << "Scadenze incompatibili con il mese commerciale\n";
}
const char* TPagamento::desc_tpr() const
{
const char* o;
switch (_tpr)
{
case 0: o = "Totale su tutte le rate"; break;
case 1: o = "Tutte le imposte su 1a"; break;
case 2: o = "Tutte le spese su 1a"; break;
case 3: o = "Imposte + spese su 1a"; break;
case 4: o = "Solo imposte"; break;
case 5: o = "Solo spese"; break;
case 6: o = "Imposte + spese"; break;
default: o = ""; break;
}
return o;
}
const char* TPagamento::desc_tipo(int tipo, char ulc) const
{
int err = ~NOERR;
const char* o;
if (ulc > ' ')
{
const char key[] = { tipo+'0', ulc, '\0' };
TTable clr("%CLR");
clr.put("CODTAB", key);
err = clr.read();
if (err == NOERR)
o = clr.get("S0");
}
if (err != NOERR)
{
switch (tipo)
{
case 1: o = "Rimessa diretta o contanti"; break;
case 2: o = "Tratta"; break;
case 3: o = "Ricevuta Bancaria"; break;
case 4: o = "Cessione"; break;
case 5: o = "Paghero'"; break;
case 6: o = "Lettera di credito"; break;
case 7: o = "Tratta accettata"; break;
case 8: o = "Rapporti interban. diretti"; break;
case 9: o = "Bonifici"; break;
default: o = "Altro pagamento"; break;
}
}
return o;
}
word TPagamento::recalc_rate(int row, bool is_perc_modified,
const char* new_value, const char* scad,
const char* typ, const char* ulc,
int rdiff, bool mcomm,
bool& need_recalc)
// ricalcola le rate sulla base di parametri modificati sulla riga row
// parametri: tutti i const char* possono essere NULL, il che vuol dire
// che i dati corrispondenti non sono stati modificati;
// se new_value non e' NULL puo' essere la percentuale (e
// allora is_perc_modified e' TRUE) o l'importo. Non e'
// possibile modificare entrambi; se succede viene data
// priorita' alla percentuale.
{
CHECK(!(!is_perc_modified && new_value != NULL && !_inited),
"A'stronzo! E famme 'na pippa! Me dai n'importo che nun ce sta? Ma Vaffanculo!");
if (_rate.items() == 0) return P_OK;
real rsum(0.0), newv(0.0), rmax(0.0);
int oldtype = tipo_rata(0);
TString oldulc = ulc_rata(0);
int oldscad = scad_rata(0);
TDate lastdate = data_rata(0);
int first = _tpr < 4 ? 0 : 1;
TString_array srate(_rate); // rate come erano
if (srate.items() > 1)
{
// calcola defaults per tipo pagamento e scadenza
// nel caso di rate nuove
oldscad = scad_rata(1);
if (_mcomm)
{
int mesi = oldscad / 30;
if (mesi == 0) mesi = 1;
oldscad = 30 * mesi;
}
}
if (oldscad <= 0) oldscad = _int_rate;
if (oldtype <= 0) oldtype = _def_tpr;
if (new_value != NULL)
{
newv = new_value;
rmax = is_perc_modified ? real(100.0) : (_tpr < 4 ? _firstr : _secndr);
if (newv > rmax) return P_RSUM;
}
bool exhausted = FALSE;
for (int i = 0 /* first */; i < srate.items(); i++)
{
if (i == row)
{
if (typ != NULL)
{
for (int k = 0; k < srate.items(); k++)
{
// deve farlo per tutte dalla 1 in poi se rdiff == 2,
// soltanto per row diversamente
if ((rdiff == 2 && row > 0 && k > 0) || k == row)
{
TToken_string& tt = rata(k);
TToken_string& ss = (TToken_string&)srate[k];
tt.add(typ,2);
ss.add(typ,2);
tt.add(ulc,5);
ss.add(ulc,5);
need_recalc = TRUE;
// no error is possible
}
}
}
if (ulc != NULL)
{
for (int k = 0; k < srate.items(); k++)
{
// deve farlo per tutte dalla 1 in poi se rdiff == 2,
// soltanto per row diversamente
if ((rdiff == 2 && row > 0 && k > 0) || k == row)
{
TToken_string& tt = rata(k);
TToken_string& ss = (TToken_string&)srate[k];
tt.add(ulc,5);
ss.add(ulc,5);
need_recalc = TRUE;
// no error is possible
}
}
}
if (scad != NULL)
{
// if !_inited scad e' il n. giorni, se no e' la rata
if (_inited)
{
TToken_string& tt = rata(row);
TToken_string& ss = (TToken_string&)srate[row];
lastdate = scad;
// controlla errore sulla data scadenza
if (i > 0)
{
oldscad = (int)(lastdate - data_rata(i-1));
if (oldscad <= 0l) return P_SCAD;
}
else if (lastdate < _inizio) return P_INIZIO;
tt.add(scad,3);
ss.add(scad,3);
// ricalcola rate successive: se si vuole modificarne solo una
// ci si fotte e si disabilita il ricalcolo
TDate ddd (lastdate);
for (int j = row+1; j < srate.items(); j++)
{
TToken_string& tt = rata(j);
TToken_string& ss = (TToken_string&)srate[j];
next_scad(ddd,scad_rata(j),mcomm,j);
tt.add(ddd.string(),3);
ss.add(ddd.string(),3);
need_recalc = TRUE;
}
}
else
{
// nulla di speciale visto che si memorizza la derivata
int sc = atoi(scad);
for (int i = 0; i < row; i ++)
sc -= scad_rata(i);
if (sc < 0 || (row > 0 && sc == 0)) return P_SCAD;
if (_mcomm && (sc % 30) != 0)
return P_MCOMM;
TToken_string& tt = rata(row);
TToken_string& ss = (TToken_string&)srate[row];
tt.add(sc,0);
ss.add(sc,0);
need_recalc = TRUE;
}
}
// here's the bell
if (new_value != NULL)
{
real totogl((row == first && _inited && _tpr > 0 && _tpr < 4) ? _secndr : 0.0);
if (newv == ZERO || (rsum+newv-totogl) > rmax)
return P_RSUM;
// did not sforate
rsum += newv-totogl;
TToken_string& rt = rata(row);
// setta nuovo valore e ricalcola cio' che ne consegue
if (is_perc_modified) rt.add(new_value,1);
else rt.add(new_value,4);
// riaggiusta le rate rimanenti
real remainder(0.0); remainder = rmax - rsum;
if (!(exhausted = (remainder == real(0.0))))
{
// se inited e scelto UGUALI (2) occorre dividere in N e
// aggiungere il resto sulla 1a rata
// controlla se rdiff e' compatibile con
// i dati e se e' il caso riaggiusta
// -------------------------------- vedi se rimettere -----------------------------------
// if (rdiff == 3 && (remainder % newv) != 0.0)
// {
// warning_box("Il resto non e' divisibile - uso 'Uguali finche' possibile'");
// rdiff = 4;
// }
// ---------------------------------------------------------------------------------------
// *** 10/8/95: se uguali e non e' multiplo intero lo teniamo cosi' e poi
// *** aggiungiamo alla prima rata utile
// if (rdiff == 2 && !((rmax % newv.integer()) == ZERO))
// rdiff = 1;
// *** 5/9/85: UGUALI (2) significa la seguente menata: se c'e' una sola rata
// *** uguale al totale importo, come prima; diversamente, la prima rata
// *** va mantenuta uguale comunque sia, e occorre ridefinire le restanti in
// *** modo che siano uguali. In questo caso deve far fede il NUMERO di RATE
// *** indicato, che in prima nota non c'e' e va quindi inserito (porcoddio).
// si registrano rate UGUALI soltanto se specificato UGUALI
// il che non e' sorprendente, ma coi tempi che corrono...
_rdiff = (rdiff == 1 || rdiff == 3 || rdiff == 4);
// procedi
if (rdiff == 1)
{
// cancella tutte le rate successive, aggiungi un'unica rata
// con il resto dell'importo
if (row < (srate.items()-1))
{
TToken_string& trt = rata(row+1);
trt.add(remainder.string(), is_perc_modified ? 1 : 4);
for(int j = row+2; j < srate.items(); j++)
_rate.destroy(j);
}
else
{
// l'importante e' esagerare
for(int j = row+1; j < srate.items(); j++)
_rate.destroy(j);
TToken_string& trt = add_rata(is_perc_modified ? remainder : (real) 0.0,
oldscad, oldtype);
if (!is_perc_modified) trt.add(remainder.string(),4);
if (_inited)
{
TDate dd = data_rata(row);
next_scad(dd,oldscad,mcomm,row);
trt.add(dd.string(),3);
}
}
}
else // rate non differenziate (dall'inizio o da row)
{
// ripartisci l'importo nel numero necessario di rate per
// mantenere costante il valore
real sum(0.0);
if (_inited) lastdate = data_rata(rdiff == 2 ? first : row);
TDate dd(lastdate);
int type = oldtype;
TString nulc = oldulc;
int nscd = oldscad;
int frstd = scad_rata(0);
const int frs = (rdiff == 3 || rdiff == 4) ? row+1 : first;
const real mx = (rdiff == 3 || rdiff == 4) ? remainder : rmax;
// cancelliamo tutto, va'
for (int j = srate.items()-1; j >= frs; j--) _rate.destroy(j);
const int n = srate.items(); // questo rimane per forza costante
if (rdiff == 2 && n > 1+first)
{
// HERE
real tot = is_perc_modified ? real(100.0) : (_tpr < 4 ? _firstr : _secndr);
real delta = is_perc_modified ? ZERO :
((_tpr > 0 && _tpr < 4) ? _secndr : ZERO);
real nimp(new_value);
// se ho modificato la prima, divido il resto nelle successive
// lasciando costante il numero rate
if (row == first)
{
if (!delta.is_zero()) nimp -= delta;
// inferiore al minimo
if (nimp.sign() < 0) { _rate = srate; return P_NEG; }
real remainder = tot - nimp;
real div = remainder / real(n - 1 - first);
div.round(is_perc_modified ? 2 : _round);
nimp = tot - (div * real(n-1-first));
for (int k = first; k < n; k++)
{
TToken_string& trt = (TToken_string&)srate[k];
if (_inited) dd = trt.get(3);
type = atoi(trt.get(2));
nscd = k == 0 ? frstd : atoi(trt.get(0));
if (type == 0) type = 1;
if (k > 0 && nscd == 0) nscd = oldscad;
if (_inited && dd == lastdate && k > 0)
next_scad(dd,nscd,mcomm,k);
set_rata(k, is_perc_modified ? (k == first ? nimp : div) : ZERO,
nscd, type, nulc, NULL, _inited ? dd.string() : NULL);
if (!is_perc_modified)
set_imprata (k, k == first ? (nimp + delta) : div);
}
}
else
{
// se ho modificato la seconda o oltre, faccio tutte uguali dalla
// seconda in poi; la prima resta com'e' se possibile, mentre se
// c'e' un resto glielo sommo.
// Dunque: prendo la prima, calcolo il resto, divido per quella nuova,
// se resto lo sommo alla prima, e faccio quante rate servono
TToken_string& trt = (TToken_string&)srate[first];
real rfirst(is_perc_modified ? trt.get(1) : trt.get(4));
if (!delta.is_zero()) rfirst -= delta;
real rest = tot - rfirst;
real div = rest / nimp;
if (div < real(1.0)) { _rate = srate; return P_TROP; }
int nr = (int)div.integer() + (_tpr > 3 ? 2 : 1);
real reminder = rest - (nimp * real(nr - 1 - first));
rfirst += reminder;
for (int k = first; k < nr; k++)
{
nscd = oldscad;
type = _def_tpr;
TString16 ulc(_def_ulc);
if (srate.items() > k)
{
TToken_string& trt = (TToken_string&)srate[k];
if (_inited) dd = trt.get(3);
type = atoi(trt.get(2));
nscd = atoi(trt.get(0));
ulc = trt.get(5);
}
if (type == 0) type = 1;
if (_inited && dd == lastdate && k > 0)
next_scad(dd,nscd,mcomm,k);
set_rata(k, is_perc_modified ? (k == first ? rfirst : nimp) : ZERO,
nscd, type, ulc, NULL, dd.string());
if (!is_perc_modified)
set_imprata (k, k == first ? (rfirst + delta) : nimp);
}
}
}
else if (rdiff != 4)
for (j = frs; sum < mx; j++)
{
// se c'e' la vecchia rata si tengono i parametri
// altrimenti si calcolano
if (j < srate.items())
{
TToken_string& trt = (TToken_string&)srate[j];
if (_inited) dd = trt.get(3);
type = atoi(trt.get(2));
nscd = atoi(trt.get(0));
if (type == 0) type = 1;
if (j > 0 && nscd == 0) nscd = oldscad;
if (_inited && dd == lastdate && j > frs)
next_scad(dd,nscd,mcomm,j);
}
else if (_inited)
{
if (dd <= botime) dd = lastdate;
next_scad(dd,nscd,mcomm,j);
}
else nscd = _int_rate;
TToken_string& ttr = set_rata(j, is_perc_modified ? newv : ZERO,
nscd, type);
if (_inited)
ttr.add(dd.string(), 3);
if (!is_perc_modified)
{
ttr.add(newv.string(),4);
}
if ((mx - sum) < newv)
{
// add remainder on first rate
newv += (mx - sum);
if (rdiff == 3)
{
newv = is_perc_modified ? perc_rata(first) : tpay_rata(first);
newv += (mx - sum);
}
if (!is_perc_modified)
set_imprata(first, newv);
else {
TToken_string& t = rata(first);
t.add(newv.string(), 1);
}
remove_rata(j);
break;
}
sum += newv;
}
else // rdiff == 4; uguali finche' possibile
{
bool basta = FALSE;
for (j = frs; ; j++)
{
// ultima rata puo' differire dalle precedenti
if (mx - sum <= newv)
{
newv = mx - sum;
basta = TRUE;
}
// se c'e' la vecchia rata si tengono i parametri
// altrimenti si calcolano
if (j < srate.items())
{
TToken_string& trt = (TToken_string&)srate[j];
if (_inited) dd = trt.get(3);
type = atoi(trt.get(2));
nscd = atoi(trt.get(0));
if (type == 0) type = 1;
if (j > 0 && nscd == 0) nscd = oldscad;
if (_inited && dd == lastdate && j > 0)
next_scad(dd,nscd,mcomm,j);
}
else if (_inited)
next_scad(dd,nscd,mcomm,j);
TToken_string& ttr = set_rata(j, is_perc_modified ? newv : ZERO,
nscd, type);
if (_inited)
ttr.add(dd.string(), 3);
if (!is_perc_modified)
ttr.add(newv.string(),4);
if (basta) break;
sum += newv;
}
}
}
}
else // exhausted
{
for(int j = row+1; j < srate.items(); j++)
_rate.destroy(j);
}
if (_inited)
{
// ricalcola il valore secondario (non modificato)
real toshare(100.0);
if (is_perc_modified)
toshare = (_tpr < 4 ? _firstr : _secndr);
TDistrib dt(toshare, is_perc_modified ? _round : 3);
for (int j = first; j < _rate.items(); j++)
{
real rvl = is_perc_modified ? perc_rata(j) : tpay_rata(j);
// togli pezxo di troppo
if (!is_perc_modified && j == first && _tpr > 0 && _tpr < 4)
rvl -= _secndr;
real zpx = rvl/rmax; // percentuale
dt.add(zpx); // * cento???
}
for (j = first; j < _rate.items(); j++)
{
real rfirst(0.0);
TToken_string& tr = rata(j);
real rvl = dt.get();
if (j == first)
{
rfirst = rvl;
if (rdiff == 2)
{
real reminder = toshare - rfirst;
real rdiv = reminder.is_zero() ? real(0.0) :
(reminder / real(_rate.items() - (1+first)));
rdiv.round( is_perc_modified ? _round : 2);
rfirst += reminder - (rdiv * real(_rate.items() - (1+first)));
rvl = rdiv;
}
}
if (is_perc_modified && j == first && _tpr > 0 && _tpr < 4)
rfirst += _secndr;
tr.add((j == first ? rfirst.string() : rvl.string()), is_perc_modified ? 4 : 1);
if (_cambio != 1.0)
{
real implit(tpay_rata(j));
implit *= _cambio;
implit.round();
tr.add(implit.string(), 7);
}
}
// se e' il caso aggiungi l'importo fisso sulla prima rata
// if (_inited && _tpr > 0 && _tpr < 4)
// {
// TToken_string& tr = rata(0);
// real tot = tpay_rata(0) + _secndr;
// tr.add(tot.string(), 4);
// }
}
// sistema scadenze fisse
adjust_fixed_scad();
need_recalc = TRUE;
return P_OK;
} // new_value != NULL
}
else // i != row modified
{
if (rdiff == 2)
continue;
if (i > 0 && !((perc_rata(i-1) == perc_rata(i))))
{
if (rdiff == 2)
rdiff = 1;
_rdiff = TRUE;
}
if (is_perc_modified)
rsum += perc_rata(i);
else
{
rsum += tpay_rata(i);
if (i == 0 && _tpr > 0 && _tpr < 4)
rsum -= _secndr;
}
lastdate = data_rata(i);
oldtype = tipo_rata(i);
if (scad_rata(i) > 0) oldscad = scad_rata(i);
if (_inited && i > 0)
{
if (data_rata(i) <= data_rata(i-1))
return P_SCAD;
}
else if (_inited && lastdate < _inizio)
return P_INIZIO;
}
}
adjust_fixed_scad();
return P_OK;
}
bool TPagamento::read(TTable* t, TTable* r)
{
// puo' chiamarla chiunque
bool istnew = FALSE;
if (t == NULL)
{
t = new TTable("%CPG");
istnew = TRUE;
}
t->zero(); t->put("CODTAB", _code);
if (t->read() != NOERR) return FALSE;
_slicer.init(real(0.0), TRUE);
_rate.destroy();
// set everything
_rdiff = t->get_bool("B1");
_mcomm = t->get_bool("B0");
_tpr = atoi(t->get("S3"));
_inscad = *((const char*)(t->get("S1")));
_code = t->get("CODTAB");
_name = t->get("S0");
_fixd[0] = t->get_int("I0");
_fixd[1] = t->get_int("I1");
_fixd[2] = t->get_int("I2");
// aggiusta _inizio secondo INSCAD; vedi mese commerciale etc.
if (_inscad == 'M')
{
if (_mcomm) _inizio.set_day(_inizio.month() == 2 ? 28 : 30);
else _inizio.set_end_month();
}
else if (_inscad == 'F' && _mcomm && _inizio.day() == 31)
_inizio.set_day(30);
// leggi rate e scadenze
bool isrnew = FALSE;
if (r == NULL)
{
r = new TTable("%RPG");
isrnew = TRUE;
}
TString16 s;
for (int i = 0; ;i++)
{
r->zero(); s.format("%s%3d",(const char*)_code, i);
r->put("CODTAB", (const char*)s);
if (r->read() != NOERR)
break;
TToken_string* tt = new TToken_string(48);
tt->add((const char*)(r->get("I0"))); // scadenza
tt->add((const char*)(r->get("R0"))); // percentuale
tt->add((const char*)(r->get("I1"))); // tipo
// data e importo
TDate d = _inizio;
next_scad(d,(int)(r->get_long("I0")),_mcomm,i);
tt->add((const char*)d);
tt->add("");
tt->add(r->get("S1")); // ulteriore classificazione
_slicer.add((real)r->get("R0"));
_rate.add(tt);
}
if (istnew) delete t;
if (isrnew) delete r;
return TRUE;
}
int TPagamento::write(TTable& r)
{
// Scrive soltanto le righe di pagamento; si assume sia stata chiamata da una
// relapp, che ha scritto il file principale
TString16 s;
int err = NOERR;
for (int i = 0; err == NOERR && i < n_rate(); i++)
{
r.zero(); s.format("%s%3d",(const char*)_code, i);
r.put("CODTAB", s);
r.put("I0", (long)scad_rata(i));
r.put("R0", perc_rata(i).string());
r.put("I1", (long)tipo_rata(i));
r.put("S1", ulc_rata(i));
err = r.write();
}
return err;
}
int TPagamento::rewrite(TTable& r)
{
TString16 s; int err = NOERR;
for (int i = 0; err == NOERR && i < n_rate(); i++)
{
r.zero(); s.format("%s%3d",(const char*)_code, i);
r.put("CODTAB", s);
bool was = (r.read() == NOERR);
r.zero(); s.format("%s%3d",(const char*)_code, i);
r.put("CODTAB", (const char*)s);
r.put("I0", (long)scad_rata(i));
r.put("R0", perc_rata(i).string());
r.put("I1", (long)tipo_rata(i));
r.put("S1", ulc_rata(i));
err = (was ? r.rewrite() : r.write());
}
// erase possible rates > current n. rates
for (;err == NOERR;i++)
{
r.zero(); s.format("%s%3d",(const char*)_code, i);
r.put("CODTAB", (const char*)s);
if (r.read() == NOERR) err = r.remove();
else break;
}
return err;
}
int TPagamento::remove(TTable& r)
{
TString16 s; int err = NOERR;
for (int i = 0 ; err == NOERR; i++)
{
r.zero(); s.format("%s%3d", _code, i);
r.put("CODTAB", s);
if (r.read() == NOERR)
err = r.remove();
else break;
}
return err;
}
void TPagamento::set_rate_auto()
{
// vedi rate esistenti e tipo prima rata
// deve fare riferimento ad un tipo pagamento esistente
// e sensato
int first = 0;
real toslice = _firstr;
if (n_rate() == 0 || !_inited || (_tpr > 3 && n_rate() == 1))
return;
main_app().begin_wait();
if (_tpr > 3) // ripartisci _firstr su tutte le rate
{
first = 1;
toslice = _secndr;
}
_slicer.init(toslice);
if (_tpr > 3)
// prima rata obbligatoria
set_imprata(0, _firstr);
// se rate uguali dividi l'importo totale per il numero di rate
real r1(0.0), ro(0.0);
if (!_rdiff)
{
int rut = _tpr > 3 ? n_rate() - 1 : n_rate();
// usa la percentuale per la prima rata
r1 = (toslice * perc_rata(first))/real(100.0);
r1.round(_round);
real reminder = toslice - r1;
if (!reminder.is_zero())
{
ro = reminder/real(rut-1); ro.trunc(_round);
r1 = (toslice - (ro*real(rut-1)));
}
}
for (int i = first; i < n_rate(); i++)
// setta le fette e le date di scadenza
set_imprata(i, _rdiff ? _slicer.get() : (i == first ? r1 : ro));
// se e' nei primi tre casi, si somma l'importo da non dividere alla
// prima rata
if (_tpr > 0 && _tpr < 4)
set_imprata(0, tpay_rata(0) + _secndr);
// risistema rate per scadenze fisse
adjust_fixed_scad();
main_app().end_wait();
}
void TPagamento::set_total(const real& imponibile, const real& imposta, const real& spese)
{
_imponibile = imponibile;
_imposta = imposta;
_spese = spese;
_inited = TRUE;
// istanzia _firstr e _secndr a seconda di _tpr
switch(_tpr)
{
case 0:
_firstr = _imponibile + _imposta + _spese;
_secndr = 0.0;
break;
case 1:
_secndr = _imposta;
_firstr = _imponibile + _spese;
break;
case 2:
_secndr = _spese;
_firstr = _imposta + _imponibile;
break;
case 3:
_secndr = _imposta + _spese;
_firstr = _imponibile;
break;
case 4:
_secndr = _spese + _imponibile;
_firstr = _imposta;
break;
case 5:
_secndr = _imponibile + _imposta;
_firstr = _spese;
break;
case 6:
_secndr = _imponibile;
_firstr = _imposta + _spese;
break;
}
const real toslice = _tpr > 1 ? _secndr : _firstr;
_slicer.init(toslice, TRUE);
const int first = _tpr > 3 ? 1 : 0;
for (int i = first; i < _rate.items(); i++)
{
TToken_string& t = (TToken_string&)_rate[i];
const real rr(t.get(1));
_slicer.add(rr);
}
}
void TPagamento::set_sheet(TSheet_field& sf, int sscad)
{
main_app().begin_wait();
if (_inited && _rate.items() > 0)
{
const bool in_valuta = _cambio != 1.0;
// si istanzia uno sheet di primanota
for (int i = 0; i < n_rate(); i++)
{
TToken_string& ts = sf.row(i);
ts.add((const char*)data_rata(i), 0); // 0 - Data scadenza
if (in_valuta)
{
ts.add(tlit_rata(i).string(), 1); // 1 - Importo in lire
ts.add(tpay_rata(i).string(), 2); // 2 - Importo in valuta
}
else
{
ts.add(tpay_rata(i).string(), 1); // 1 - Importo
ts.add("", 2);
}
ts.add(perc_rata(i).string(), 3); // 3 - Percentuale
const int tr = tipo_rata(i);
const char uc = ulc_rata(i)[0];
ts.add(tr, 4); // 4 - Tipo rata
ts.add(uc, 5); // 5 - Ulteriore classificazione
ts.add(desc_tipo(tr, uc), 6); // 6 - Descrizione tipo rata
// 7,8,9,10 - Banche
const bool paid = ratapagata(i);
ts.add(paid ? "X" : "", 11); // 11 - Pagaya
}
for (int d = sf.items()-1; d >= i; d--)
sf.destroy(d);
sf.enable_column(2, in_valuta);
}
else
if (_rate.items() > 0) // not inited: set edit sheet
{
sf.destroy();
for (int i = 0, scr = 0; i < n_rate(); i++)
{
TToken_string& s = sf.row(-1);
scr += scad_rata(i);
s.add(scr);
s.add(perc_rata(i).string());
const int tr = tipo_rata(i);
const char uc = ulc_rata(i)[0];
s.add(tr);
s.add(uc);
s.add(desc_tipo(tr, uc));
}
}
else // new: set with 1 or 2 rates according to tpr
{
if (_tpr > 3)
add_rata(ZERO, sscad == -1 ? 0 : sscad, _def_tpr, _def_ulc);
add_rata(real(100.0), sscad == -1 ? (_tpr < 4 ? 0 : 30) : sscad, _def_tpr, _def_ulc);
_dirty = TRUE;
for (int i = 0, scr = 0; i < n_rate(); i++)
{
TToken_string& s = sf.row(-1);
scr += scad_rata(i);
s.add(scr);
s.add(perc_rata(i).string());
const int tr = tipo_rata(i);
const char uc = ulc_rata(i)[0];
s.add(tr);
s.add(uc);
s.add(desc_tipo(tr, uc));
}
}
const bool abilita = _tpr <= 3;
// disabilita campi da non toccare sulla prima rata
if (_inited)
{
sf.enable_cell(0, 1, abilita); // importo
sf.enable_cell(0, 2, abilita); // in valuta
sf.enable_cell(0, 3, abilita); // percentuale
}
else
{
sf.enable_cell(0, 1, abilita); // percentuale
}
sf.force_update();
main_app().end_wait();
}
void TPagamento::adjust_fixed_scad()
{
if (!_inited) return;
for (int i = 0; i < n_rate(); i++)
{
TDate d = data_rata(i);
// riaggiusta se ci sono scadenze fissate
if (_fixd[0] != 0 || _fixd[1] != 0 || _fixd[2] != 0)
{
for (int i = 0; i < 3; i++)
{
if (_fixd[i] >= d.day())
{
if (d.last_day(d.month(), d.year()) >= _fixd[i])
d.set_day(_fixd[i]);
else
d.set_end_month();
break;
}
}
if (i == 3)
{
if (_fixd[0] > 0 && _fixd[0] < d.day())
{
d.set_day(_fixd[0]);
const bool chyear = d.month() == 12;
d.set_month(chyear ? 1 : d.month() + 1);
if (chyear) d.set_year(d.year() + 1);
}
}
}
TToken_string& tt = rata(i);
tt.add(d.string(), 3);
}
}
TPagamento::TPagamento(const char* codtab, const char* data) :
_slicer(0.0,0), _new(FALSE), _mcomm(FALSE), _imponibile(0.0), _imposta(0.0),
_spese(0.0), _cambio(1.0), _code(codtab), _dirty(FALSE), _inited(FALSE),
_def_tpr(1), _def_ulc(""), _round(0), _int_rate(30), _tpr(0)
{
_fixd[0] = _fixd[1] = _fixd[2] = 0;
if (data != NULL && *data)
_inizio = data;
else
_inizio = TDate(TODAY);
if (_code.blank() || !read())
_new = TRUE;
}