#include "pagament.h" #include int Pagamento::_rata_ifield(int n, int f) const { TToken_string& t = (TToken_string&)_rate[n]; return t.get_int(f); } real Pagamento::_rata_rfield(int n, int f) const { TToken_string& t = (TToken_string&)_rate[n]; return real(t.get(f)); } TDate Pagamento::_rata_dfield(int n, int f) const { TToken_string& t = (TToken_string&)_rate[n]; return TDate(t.get(f)); } const char* Pagamento::_rata_sfield(int n, int f) const { static char kak[6]; TToken_string& t = (TToken_string&)_rate[n]; strcpy(kak,t.get(f)); return kak; } bool Pagamento::ratapagata(int n) { TToken_string& t = (TToken_string&)_rate[n]; return t.items() > 6; } void Pagamento::set_intervallo_rate(int in) { _dirty = TRUE; if (_mcomm && (in % 30) != 0) _mcomm = FALSE; for (int i = 0; i < n_rate(); i++) { TToken_string& ts = rata(i); ts.add(i == 0 ? (scad_rata(0) == 0 ? "0" : format("%d",in)) : format("%d",in), 0); } } void Pagamento::set_mese_commerciale(bool v, int& sscad) { _dirty = FALSE; if (_mcomm == v) return; if (sscad == -1) sscad = 30; if ((sscad % 30) != 0) sscad = 30 * ((sscad/30)+1); set_intervallo_rate(sscad); _mcomm = v; } void Pagamento::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 == 0 ? 0 : 1; i < n_rate(); i++) { TToken_string& tt = rata(i); tt.add(p,1); } _rdiff = !(v == 2); _dirty = TRUE; } } void Pagamento::set_tipo_prima_rata(int v, int sscad) { _dirty = FALSE; if (_tpr == v) return; if (v == 0 && _tpr > 0) { 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.add(NULL,0); _rate.pack(); } else if ( _tpr == 0 && v > 0) { 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); } } _tpr = v; _dirty = TRUE; } void Pagamento::set_numero_rate(int n, int sscad) { _dirty = FALSE; if (n == 0 || n == n_rate()) return; int p = 100 / n; int nr = n_rate(); int first = _tpr == 0 ? 0 : 1; for (int i = first, sum = 0; sum < 100; i++) { if ((100 - sum) < p) p = 100 - sum; sum += p; 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 < n_rate(); i++) _rate.add(NULL,i); _rate.pack(); _dirty = TRUE; } void Pagamento::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 Pagamento::remove_rata(int i) { // non fa nessun ricalcolo, si limita ad impacchettare se // necessario _rate.add(NULL,i); _rate.pack(); _dirty = TRUE; } TToken_string& Pagamento::add_rata(real perc, int day, int type) { TToken_string* tt = new TToken_string(16); tt->add(day); // scadenza tt->add(perc.string()); // percentuale tt->add(type); // tipo tt->add(""); tt->add(""); tt->add(""); _rate.add(tt); _dirty = TRUE; return *tt; } TToken_string& Pagamento::set_rata (int index, real perc, int day, int type, const char* ulc, const char* imp, const char* data) { bool nwr = FALSE; TToken_string* tt = (TToken_string*)_rate.objptr(index); if (nwr = (tt == NULL)) tt = new TToken_string(16); 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 (!nwr) { if (index > _rate.items()) { error_box("Rate non contigue"); delete tt; } } else { _rate.add(tt,index); _dirty = TRUE; } return *tt; } void Pagamento::set_imprata(int i, 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); } TToken_string& Pagamento::set_rata(int index, real howmuch, TDate& date, int type,const char* ulc, bool pagato) { // calcola percentuali e scadenze a partire dagli importi bool nwr = FALSE; TToken_string* tt = (TToken_string*)_rate.objptr(index); if (nwr = (tt == NULL)) tt = new TToken_string(16); TDate oldd = index > 0 ? data_rata(index -1) : _inizio; int day = date - oldd; real toshare(_tpr == 0 ? _firstr : _secndr); real perc = (_tpr > 0 && index == 0) ? 0.0 : howmuch/toshare; perc *= real(100.0); perc.round(2); tt->add(day,0); // scadenza tt->add(perc.string(),1); // percentuale tt->add(type,2); // tipo tt->add(date.string(),3); tt->add(howmuch.string(),4); tt->add(ulc,5); if (pagato) tt->add("X",6); if (!nwr) { if (index > _rate.items()) { error_box("Rate non contigue"); delete tt; } } else { _rate.add(tt,index); _dirty = TRUE; } return *tt; } word Pagamento::validate() const { word res = 0x0000; real r(0.0); int first = _tpr == 0 ? 0 : 1; real toshare(_tpr == 0 ? _firstr : _secndr); TDistrib ds(toshare,0); // check percentages & prepare slicer for (int i = first; i < n_rate(); i++) { real p(perc_rata(i)); ds.add(p); r += p; } if (r != real(100.0)) res |= P_RSUM; if (_inited) { ds.init(toshare); // check importi rate consistenti con la percentuale for (int i = first; i < n_rate(); i++) { real r1(tpay_rata(i)); real r2(ds.get()); if (r1 != r2) { res |= P_IMPNC; break; } } // 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 Pagamento::strerr(word err, TString& s) { s = "Errore:"; if (err & P_RSUM) s << "\n Le percentuali non sommano a 100"; if (err & P_IMPNC) s << "\n Le percentuali sono inconsistenti con gli importi"; if (err & P_SCAD) s << "\n Le scadenze non sono consecutive"; if (err & P_INIZIO) s << "\n La prima rata e' antecedente alla data movimento"; } const char* Pagamento::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 = "Spese + merce su 1a"; break; case 5: o = "Merce + imposte su 1a"; break; case 6: o = "Tutta la merce su 1a"; break; } return o; } const char* Pagamento::desc_tipo(int i) const { const char* o; switch (i) { case 1: o = "Rimessa diretta / 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 = "Altro pagamento"; break; } return o; } word Pagamento::recalc_rate(int row, bool is_perc_modified, const char* new_value, const char* scad, const char* typ, 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); int oldscad = scad_rata(0); TDate lastdate = data_rata(0); int first = _tpr == 0 ? 0 : 1; TArray 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 (oldscad == 0) oldscad = 30; if (oldtype == 0) oldtype = 1; if (new_value != NULL) { newv = new_value; rmax = is_perc_modified? (real) 100.0 : (_tpr == 0 ? _firstr : _secndr); if (newv > rmax) return P_RSUM; } bool exhausted = FALSE; for (int i = first; i < srate.items(); i++) { if (i == row) { if (typ != NULL) { TToken_string& tt = rata(row); TToken_string& ss = (TToken_string&)srate[row]; tt.add(typ,2); ss.add(typ,2); // 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) _mcomm = FALSE; 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) { if (newv == ZERO || (rsum+newv) > rmax) return P_RSUM; // did not sforate rsum += newv; 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)))) { // controlla se rdiff e' compatibile con // i dati e se e' il caso riaggiusta if (rdiff == 3 && !((remainder % newv.integer()) == ZERO)) rdiff = 2; if (rdiff == 2 && !((rmax % newv.integer()) == ZERO)) rdiff = 1; _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.add(NULL,j); } else { // l'importante e' esagerare for(int j = row+1; j < srate.items(); j++) _rate.add(NULL,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; int nscd = oldscad; int frs = (rdiff == 3 || rdiff == 4) ? row+1 : first; real mx = (rdiff == 3 || rdiff == 4) ? remainder : rmax; // cancelliamo tutto, va' for (int j = frs; j < srate.items(); j++) _rate.add(NULL,j); 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 = j == 0 ? 0 : 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) { if (dd <= botime) dd = lastdate; 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); 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 = j == 0 ? 0 : 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.add(NULL,j); } if (_inited) { // ricalcola il valore secondario (non modificato) real toshare(100.0); if (is_perc_modified) toshare = (_tpr == 0 ? _firstr : _secndr); TDistrib dt(toshare,0); for (int j = first; j < _rate.items(); j++) { real rvl = is_perc_modified ? perc_rata(j) : tpay_rata(j); real zpx = rvl/rmax; // percentuale dt.add(zpx); } for (j = first; j < _rate.items(); j++) { TToken_string& tr = rata(j); real rvl = dt.get(); tr.add(rvl.string(), is_perc_modified ? 4 : 1); } } need_recalc = TRUE; return P_OK; } // new_value != NULL } else // i != row modified { if (i > 0 && !((perc_rata(i-1) == perc_rata(i)))) { if (rdiff == 2) rdiff = 1; _rdiff = FALSE; } if (is_perc_modified) rsum += perc_rata(i); else rsum += tpay_rata(i); lastdate = data_rata(i); oldtype = tipo_rata(i); oldscad = scad_rata(i); if (_inited && i > 0) { if (data_rata(i) <= data_rata(i-1)) return P_SCAD; } else if (lastdate < _inizio) return P_INIZIO; } } return P_OK; } bool Pagamento::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; // set everything _rdiff = t->get_bool("B1"); _mcomm = t->get_bool("B0"); _tpr = *((const char*)(t->get("S3"))); _inscad = *((const char*)(t->get("S1"))); _code = t->get("CODTAB"); _name = t->get("S0"); // TBI aggiusta _inizio secondo INSCAD; vedi mese commerciale etc. if (_inscad == 'M') { if (_mcomm) _inizio.set_month(_inizio.month() == 2 ? 28 : 30); else _inizio.set_end_month(); } else if (_inscad == 'F' && _mcomm && _inizio.month() == 31) _inizio.set_month(30); // leggi rate e scadenze bool isrnew = FALSE; if (r == NULL) { r = new TTable("%RPG"); isrnew = TRUE; } TString s(16); 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(16); 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")); _slicer.add((real)r->get("R0")); _rate.add(tt); } if (istnew) delete t; if (isrnew) delete r; return TRUE; } int Pagamento::write(TTable& r) { // Scrive soltanto le righe di pagamento; si assume sia stata chiamata da una // relapp, che ha scritto il file principale TString s(16); 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", (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 = r.write(); } return err; } int Pagamento::rewrite(TTable& r) { TString s(16); 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", (const char*)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 Pagamento::remove(TTable& r) { TString s(16); int err = NOERR; for (int i = 0 ; 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; } void Pagamento::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 > 0 && n_rate() == 1)) return; if (_tpr > 1) // ripartisci _firstr su tutte le rate { first = 1; toslice = _secndr; } _slicer.init(toslice); if (_tpr > 0) // prima rata obbligatoria set_imprata(0, _firstr); for (int i = first; i < n_rate(); i++) // setta le fette e le date di scadenza set_imprata(i, _slicer.get()); } void Pagamento::set_total(real& imponibile, real& imposta, 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: _firstr = _imposta; _secndr = _imponibile + _spese; break; case 2: _firstr = _spese; _secndr = _imposta + _imponibile; break; case 3: _firstr = _imposta + _spese; _secndr = _imponibile; break; case 4: _firstr = _spese + _imponibile; _secndr = _imposta; break; case 5: _firstr = _imponibile + _imposta; _secndr = _spese; break; case 6: _firstr = _imponibile; _secndr = _imposta + _spese; break; } for (int i = 0; i < _rate.items(); i++) { TToken_string& t = (TToken_string&)_rate[i]; real rr(t.get(1)); _slicer.add(rr); } } void Pagamento::set_sheet(TSheet_field& sf, int sscad) { sf.reset(); if (_inited && _rate.items() > 0) { // si istanzia uno sheet di primanota for (int i = 0; i < n_rate(); i++) { TToken_string* ts = new TToken_string(36); // istanzia, o stronzo ts->add((const char*)data_rata(i)); ts->add(perc_rata(i).string()); ts->add(tpay_rata(i).string()); ts->add(tipo_rata(i)); ts->add(desc_tipo(tipo_rata(i))); sf.row(-1) = (*ts); if (ratapagata(i)) { sf.disable_cell(1,1); // percentuale sf.disable_cell(1,2); // importo } } } else if (_rate.items() > 0) // not inited: set edit sheet { for (int i = 0, scr = 0; i < n_rate(); i++) { TToken_string* s = new TToken_string(32); scr += scad_rata(i); s->add(format("%d",scr)); s->add(perc_rata(i).string()); s->add(format("%d",tipo_rata(i))); s->add(desc_tipo(tipo_rata(i))); s->add(ulc_rata(i)); sf.row(-1) = *s; } } else // new: set with 1 or 2 rates according to tpr { if (_tpr > 0) add_rata(ZERO, sscad == -1 ? 0 : sscad, 1); add_rata(real(100.0), sscad == -1 ? (_tpr == 0 ? 0 :30) : sscad, 1); _dirty = TRUE; for (int i = 0, scr = 0; i < n_rate(); i++) { TToken_string* s = new TToken_string(32); scr += scad_rata(i); s->add(format("%d",scr)); s->add(perc_rata(i).string()); s->add(format("%d",tipo_rata(i))); s->add(desc_tipo(tipo_rata(i))); s->add(ulc_rata(i)); sf.row(-1) = *s; } } if (_tpr > 0) { // disabilita campi da non toccare sulla prima rata if (_inited) { sf.disable_cell(0,1); // percentuale sf.disable_cell(0,2); // importo } else { sf.disable_cell(0,1); // percentuale } } sf.force_update(); } Pagamento::Pagamento(const char* codtab, const char* data) : _slicer(0.0,0), _new(FALSE), _imponibile(0.0), _imposta(0.0), _spese(0.0), _code(codtab), _dirty(FALSE), _inited(FALSE) { if (data != NULL) _inizio = data; if (_code.empty() || !read()) _new = TRUE; if (_new && data != NULL) error_box("Modalita' pagamento inesistente"); }