#include "saldacon.h" #include #include #include #include /////////////////////////////////////////////////////////// // TTree_rectype /////////////////////////////////////////////////////////// TTree_rectype::TTree_rectype(const TRectype& testa, const TRectype& riga, const char* num) : TRectype(testa), _recarr(riga, num) { } TTree_rectype::TTree_rectype(int testa, int riga, const char* num) : TRectype(testa), _recarr(riga, num) { } TTree_rectype::TTree_rectype(const TTree_rectype& t) : TRectype(t), _recarr(t._recarr) { } TObject* TTree_rectype::dup() const { TTree_rectype* r = new TTree_rectype(*this); return r; } void TTree_rectype::copy_key_to_row(TRectype& row) const { const int numkey = 0; // Memento! Gli indici delle chiavi partono da zero! RecDes* recd = rec_des(); // Descrizione del record della testata row.zero(); const KeyDes& kd = recd->Ky[numkey]; for (int i = recd->Ky[numkey].NkFields-1; i >= 0; i--) { const int nf = kd.FieldSeq[i] % MaxFields; const RecFieldDes& rf = recd->Fd[nf]; const char* name = rf.Name; const TString& val = get(name); row.put(name, val); } } int TTree_rectype::fill_array() { TRectype* row = (TRectype*)_recarr.key().dup(); copy_key_to_row(*row); const int err = _recarr.read(row); return err; } int TTree_rectype::read(TBaseisamfile& f, word op) { int err = TRectype::read(f, op); if (err == NOERR) fill_array(); else _recarr.destroy_rows(); return err; } int TTree_rectype::next(TBaseisamfile& f) { int err = TRectype::next(f); if (err == NOERR) fill_array(); else _recarr.destroy_rows(); return err; } int TTree_rectype::write(TBaseisamfile& f) const { int err = TRectype::write(f); if (err == NOERR) err = _recarr.write(); return err; } int TTree_rectype::rewrite(TBaseisamfile& f) const { int err = TRectype::rewrite(f); if (err == NOERR) err = _recarr.rewrite(); return err; } int TTree_rectype::remove(TBaseisamfile& f) { int err = TRectype::remove(f); if (err == NOERR) err = _recarr.remove(); return err; } /////////////////////////////////////////////////////////// // TRiga_scadenze /////////////////////////////////////////////////////////// TRiga_scadenze::TRiga_scadenze(TRiga_partite* r) : TTree_rectype(LF_SCADENZE, LF_PAGSCA, "NRIGP"), _riga(r) { CHECK(_riga, "Riga nulla"); } TRiga_scadenze::TRiga_scadenze(const TRiga_scadenze& s) : TTree_rectype(s), _riga(s._riga) { CHECK(_riga, "Riga nulla"); } TPartita& TRiga_scadenze::partita() const { return riga().partita(); } // Controlla se la fattura della rata e' in valuta bool TRiga_scadenze::in_valuta() const { return riga().get(PART_CODVAL).not_empty(); } // Controlla se la rata e' stata completamente pagata int TRiga_scadenze::pagata() const { for (int p = last(); p > 0; p = pred(p)) { const TRectype& pag = row(p); if (pag.get_char("ACCSAL") == 'S') break; } return p; } // Calcola il totale dei pagamenti (eventualmente in valuta) TImporto TRiga_scadenze::importo_pagato(bool val) const { const char* imp_field = val && in_valuta() ? PAGSCA_IMPORTOVAL : PAGSCA_IMPORTO; const TPartita& game = partita(); TImporto totale; for (int p = last(); p > 0; p = pred(p)) { const TRectype& pag = row(p); // Riga pagamento const TRiga_partite& sum = game.riga(p); // Riga partite const TImporto imp(sum.sezione(), pag.get_real(imp_field)); totale += imp; } return totale; } // Calcola l'importo da pagare (eventualmente in valuta) TImporto TRiga_scadenze::importo_da_pagare(bool val) const { const char* imp_field = val && in_valuta() ? PAGSCA_IMPORTOVAL : PAGSCA_IMPORTO; const TRiga_partite& r = riga(); // Riga fattura const TImporto totale(r.sezione(), get_real(imp_field)); return totale; } // Calcola l'abbuono della rata e ritorna il suo tipo: // 'A' abbuono attivo; 'P' abbuono passivo // La sezione dell'abbuono calcolato e' quella della riga contabile in cui finira' char TRiga_scadenze::calcola_abbuono(TImporto& abbuono, bool val) const { bool ap = ' '; if (pagata()) { abbuono = importo_da_pagare(TRUE); abbuono += importo_pagato(TRUE); const int sign = abbuono.valore().sign(); if (sign != 0) { if (sign > 0) ap = abbuono.sezione() == 'D' ? 'P' : 'A'; else ap = abbuono.sezione() == 'D' ? 'A' : 'P'; } if (val && in_valuta()) { abbuono.valore() *= riga().get_real(PART_CAMBIO); abbuono.valore().round(); } } else abbuono.valore() = ZERO; return ap; } // Calcola la differenza cambi con la sezione da mettere nella riga contabile corrispondente TImporto TRiga_scadenze::calcola_differenza_cambio(bool update) { TImporto diffcam; if (in_valuta()) { const int riga_saldo = pagata(); if (riga_saldo > 0) { TRectype& pag = row(riga_saldo); const TRiga_partite& sum = partita().riga(riga_saldo); const char sez = sum.sezione(); if (update) { diffcam = importo_da_pagare(FALSE); diffcam += importo_pagato(FALSE); real a = pag.get_real(PAGSCA_ABBUONI); if (in_valuta()) { a *= riga().get_real(PART_CAMBIO); a.round(); } const TImporto abb_lit(sez, a); diffcam += abb_lit; diffcam.normalize(sez); pag.put(PAGSCA_DIFFCAM, diffcam.valore()); } else { diffcam.set(sez, pag.get_real(PAGSCA_DIFFCAM)); } } else if (update) { for (int p = last(); p > 0; p = pred(p)) { TRectype& pag = row(p); pag.zero(PAGSCA_DIFFCAM); } } } return diffcam; } real TRiga_scadenze::residuo(bool val) const { TImporto residuo(importo_da_pagare(val)); residuo += importo_pagato(val); // Somma con sezione opposta return residuo.valore(); } bool TRiga_scadenze::modifica_pagamento(const TRectype& new_pag, char& old_ap, TImporto& old_abb, TImporto& old_diffcam, char& new_ap, TImporto& new_abb, TImporto& new_diffcam) { const int nrigp = new_pag.get_int(PAGSCA_NRIGP); const TRectype old_pag(row(nrigp)); TRiga_partite& sum = partita().riga(nrigp); TImporto old_abbuono; old_ap = calcola_abbuono(old_abbuono, TRUE); // Vecchio abbuono in valuta old_abb = old_abbuono; // Vecchio abbuono in lire if (in_valuta()) { old_abb.valore() *= riga().get_real(PART_CAMBIO); old_abb.valore().round(); } old_diffcam = calcola_differenza_cambio(FALSE); const char* imp_field = in_valuta() ? PAGSCA_IMPORTOVAL : PAGSCA_IMPORTO; const real importo(new_pag.get(imp_field)); const bool empty = importo.is_zero(); if (empty) rows_array().destroy_row(nrigp); else row(nrigp) = new_pag; TImporto new_abbuono; new_ap = calcola_abbuono(new_abbuono, TRUE); // Calcolo abbuono in valuta new_abb = new_abbuono; // Calcola nuovo abbuono in lire if (in_valuta()) { new_abb.valore() *= riga().get_real(PART_CAMBIO); new_abb.valore().round(); } // Scambia sezione per registrazione contabile old_abbuono.swap_section(); new_abbuono.swap_section(); new_abbuono.normalize(sum.sezione()); if (new_ap != ' ') { CHECK(nrigp == pagata(), "Aggiornamento abbuoni inconsistente"); TRectype& pag = row(nrigp); pag.put(PAGSCA_ABBUONI, new_abbuono.valore()); } sum.update(old_abbuono, new_abbuono, PART_SEZABB, PART_ABBUONI); new_diffcam = calcola_differenza_cambio(TRUE); // Memorizza differenza cambi invertita, mettendo new_diffcam prima di old_diffcam! sum.update(new_diffcam, old_diffcam, PART_SEZDIFCAM, PART_DIFFCAM); sum.update(old_pag, new_pag, PART_IMPORTO); sum.update(old_pag, new_pag, PART_IMPORTOVAL); sum.update(old_pag, new_pag, PART_RITENUTE); partita().chiusa(TRUE); // Aggiorna flag di chiusura return empty; } bool TPartita::modifica_pagamento(const TRectype& new_pag) { char old_ap, new_ap; TImporto old_abbuono, new_abbuono, old_diffcam, new_diffcam; return modifica_pagamento(new_pag, old_ap, old_abbuono, old_diffcam, new_ap, new_abbuono, new_diffcam); } /////////////////////////////////////////////////////////// // TRiga_partite /////////////////////////////////////////////////////////// TRiga_partite::TRiga_partite(TPartita* game) : TTree_rectype(LF_PARTITE, LF_SCADENZE, SCAD_NRATA), _partita(game) { CHECK(_partita, "Partita nulla"); } TRiga_partite::TRiga_partite(const TRiga_partite& r) : TTree_rectype(r), _partita(r._partita) { CHECK(_partita, "Partita nulla"); } TRiga_scadenze& TRiga_partite::new_row(int r) { if (r <= 0) r = last()+1; if (_recarr.rows() == 0) { TRiga_scadenze* scad = new TRiga_scadenze(this); copy_key_to_row(*scad); _recarr.set_key(scad); // Altrimenti le righe sarebbero dei TRectype! } return (TRiga_scadenze&)_recarr.row(r, TRUE); } int TRiga_partite::read(TBaseisamfile& f, word op) { int err = TRectype::read(f, op); if (err == NOERR && get_int(PART_TIPOMOV) == 1) { TRiga_scadenze* s = new TRiga_scadenze(this); copy_key_to_row(*s); err = _recarr.read(s); // Deve esistere almento una scadenza } else _recarr.destroy_rows(); return err; } int TRiga_partite::ultimo_pagamento(int r) const { const TRiga_scadenze& s = rata(r); return s.last(); } bool TRiga_partite::update(const TRectype& vec, const TRectype& nuo, const char* field) { real totale(get(field)); totale -= vec.get_real(field); totale += nuo.get_real(field); put(field, totale); return totale.is_zero(); } bool TRiga_partite::update(const TImporto& vec, const TImporto& nuo, const char* sez, const char* val) { bool zero = FALSE; TImporto growth(nuo); growth -= vec; // Variazione al totale if (!growth.is_zero()) { TImporto totale(get_char(sez), get_real(val)); totale += growth; // incrementa il totale totale.normalize(); put(sez, totale.sezione()); // Aggiorna il totale sul record put(val, totale.valore()); zero = totale.is_zero(); } else zero = get_real(val).is_zero(); return zero; } /////////////////////////////////////////////////////////// // TPartita /////////////////////////////////////////////////////////// TPartita::TPartita(const TBill& clifo, int anno, const char* num) : _part(LF_PARTITE, PART_NRIGA), _unassigned(LF_PAGSCA, "NRIGP") { read(clifo, anno, num); } TPartita::TPartita() : _part(LF_PARTITE, PART_NRIGA), _unassigned(LF_PAGSCA, "NRIGP") {} // Costruisce le righe della partita bool TPartita::read(const TBill& clifo, int anno, const char* num) { TRiga_partite* partita = new TRiga_partite(this); // Record campione della partita partita->put(PART_TIPOCF, clifo.tipo()); // Tipo clifo if (clifo.tipo() <= ' ') { partita->put(PART_GRUPPO, clifo.gruppo()); // Scrivi gruppo e conto solamente partita->put(PART_CONTO, clifo.conto()); // nei conti normali (no clifo) } partita->put(PART_SOTTOCONTO, clifo.sottoconto()); // Sottoconto o codice clifo partita->put(PART_ANNO, anno); // Anno partita partita->put(PART_NUMPART, num); // Numero partita _part.read(partita); TRectype unas(LF_PAGSCA); // Record campione pagamenti non assegnati unas.zero(); unas.put(PART_TIPOCF, partita->get(PART_TIPOCF)); // Copia chiave partite unas.put(PART_GRUPPO, partita->get(PART_GRUPPO)); unas.put(PART_CONTO, partita->get(PART_CONTO)); unas.put(PART_SOTTOCONTO, partita->get(PART_SOTTOCONTO)); unas.put(PART_ANNO, partita->get(PART_ANNO)); unas.put(PART_NUMPART, partita->get(PART_NUMPART)); unas.put(PART_NRIGA, (int)UNASSIGNED); unas.put(SCAD_NRATA, (int)UNASSIGNED); _unassigned.read(unas); return ok(); } bool TPartita::reread() { TBill zio; conto(zio); const int year = anno(); const TString16 num = numero(); return read(zio, year, num); } bool TPartita::write(bool re) const { int err = _part.write(re); if (err == NOERR) err = _unassigned.write(re); return err == NOERR; } bool TPartita::remove() { _part.destroy_rows(); _unassigned.destroy_rows(); return rewrite(); } // Crea un nuova riga partite e gli copia la chiave principale e il conto cliente/fornitore TRiga_partite& TPartita::nuova_riga() { TRiga_partite& nuova = (TRiga_partite&)_part.row(last()+1, TRUE); const TRiga_partite& prima = riga(first()); nuova.put(PART_GRUPPOCL, prima.get(PART_GRUPPOCL)); nuova.put(PART_CONTOCL, prima.get(PART_CONTOCL)); return nuova; } TRiga_scadenze& TPartita::rata(int nriga, int nrata) const { if (nriga <= 0) nriga = prima_fattura(); const TRiga_partite& r = riga(nriga); return r.rata(nrata); } int TPartita::rata_pagata(int nriga, int nrata) const { int pag = 0; if (nriga != UNASSIGNED) { const TRiga_scadenze& r = rata(nriga, nrata); pag = r.pagata(); } return pag; } TRectype& TPartita::pagamento(int nriga, int nrata, int nrigp) { if (nriga == UNASSIGNED) return _unassigned.row(nrigp, TRUE); TRiga_scadenze& r = rata(nriga, nrata); return r.row(nrigp); } bool TPartita::esiste(int nriga, int nrata, int nrigp) const { if (nriga == UNASSIGNED) return _unassigned.exist(nrigp); if (nrata <= 0) return _part.exist(nriga); if (nrigp <= 0) { const TRiga_partite& r = riga(nriga); return r.rows_array().exist(nrata); } const TRiga_scadenze& r = rata(nriga, nrata); return r.rows_array().exist(nrigp); } TImporto TPartita::importo_speso(long nreg, int numrig, bool extra) const { TImporto imp; for (int r = last(); r > 0; r = pred(r)) { const TRiga_partite& part = riga(r); const long reg = part.get_long(PART_NREG); if (reg == nreg) { const int num = part.get_int(PART_NUMRIG); if (num == numrig) { const char sez = part.get_char(PART_SEZ); // Deve essere valida per forza! imp += TImporto(sez, part.get_real(PART_IMPORTO)); if (extra) { TImporto abbuoni(part.get_char(PART_SEZABB), part.get_real(PART_ABBUONI)); if (part.in_valuta()) // Se e' in valuta { abbuoni.valore() *= part.get_real(PART_CAMBIO); abbuoni.valore().round(); } imp += abbuoni; imp += TImporto(part.get_char(PART_SEZDIFCAM), part.get_real(PART_DIFFCAM)); } } } } return imp; } void TPartita::update_reg(long nreg, const TRectype& mov) { for (int r = last(); r > 0; r = pred(r)) { TRectype& pag = _part.row(r, FALSE); const long reg = pag.get_long(PART_NREG); if (reg == nreg) { pag.put(PART_NREG, mov.get(MOV_NUMREG)); pag.put(PART_DATAREG, mov.get(MOV_DATAREG)); pag.put(PART_DATADOC, mov.get(MOV_DATADOC)); pag.put(PART_NUMDOC, mov.get(MOV_NUMDOC)); pag.put(PART_REG, mov.get(MOV_REG)); pag.put(PART_PROTIVA, mov.get(MOV_PROTIVA)); pag.put(PART_CODCAUS, mov.get(MOV_CODCAUS)); pag.put(PART_CODVAL, mov.get(MOV_CODVAL)); pag.put(PART_CAMBIO, mov.get(MOV_CAMBIO)); pag.put(PART_DATACAM, mov.get(MOV_DATACAM)); } } } // Calcola la riga di movimento relativa a una riga partita int TPartita::rig2mov(int rp) const { const TRiga_partite& r = riga(rp); return r.get_int(PART_NUMRIG); } // Calcola la riga di partita relativa a una riga movimento int TPartita::mov2rig(long numreg, int rm) const { for (int r = last(); r > 0; r = pred(r)) { const TRiga_partite& row = riga(r); if (row.get_long(PART_NREG) == numreg) { if (rm <= 0 || row.get_int(PART_NUMRIG) == rm) return r; } } return -1; } // Trova la prima riga della partita contenente una fattura int TPartita::prima_fattura(long nreg) const { const int lastrow = last(); for (int r = first(); r <= lastrow; r = succ(r)) { const TRiga_partite& row = riga(r); const int tipomov = row.get_int(PART_TIPOMOV); if (tipomov == 1 || tipomov == 2) if (nreg == -1 || nreg == row.get_long(PART_NREG)) return r; } return -1; } // Trova la prima riga della partita contenente una fattura int TPartita::primo_pagamento(long nreg) const { const int lastrow = last(); for (int r = first(); r <= lastrow; r = succ(r)) { const TRiga_partite& row = riga(r); const int tipomov = row.get_int(PART_TIPOMOV); if (tipomov == 3) // TBI controllare per insoluti (tipomov == 6) if (nreg == -1 || nreg == row.get_long(PART_NREG)) return r; } return -1; } void TPartita::calcola_saldo(TImporto& saldo, TImporto& doc, TImporto& pag, TImporto& imp) const { doc = pag = imp = TImporto('D', ZERO); for (int r = last(); r > 0; r = pred(r)) { const TRiga_partite& row = riga(r); TImporto i(row.get_char(PART_SEZ), row.get_real(PART_IMPORTO)); switch (row.get_int(PART_TIPOMOV)) { case 1: case 2: doc += i; // documenti break; case 3: pag += i; // pagamenti break; default: imp += i; // altri importi break; } TImporto abbuoni(row.get_char(PART_SEZABB), row.get_real(PART_ABBUONI)); if (row.get(PART_CODVAL).not_empty()) { abbuoni.valore() *= row.get_real(PART_CAMBIO); abbuoni.valore().round(); } imp += abbuoni; imp += TImporto(row.get_char(PART_SEZDIFCAM), row.get_real(PART_DIFFCAM)); } saldo = doc; saldo += pag; saldo += imp; } bool TPartita::utilizzata(int nrigp) const { for (int p = last(); p > 0; p = pred(p)) { const TRiga_partite& fatt = riga(p); const int tipomov = fatt.get_int(PART_TIPOMOV); if (tipomov == 1) { for (int r = fatt.rate(); r > 0; r--) { const TRiga_scadenze& scad = fatt.rata(r); if (scad.rows_array().exist(nrigp)) return TRUE; } } } return _unassigned.exist(nrigp); } bool TPartita::modifica_pagamento(const TRectype& new_pag, char& old_ap, TImporto& old_abb, TImporto& old_diffcam, char& new_ap, TImporto& new_abb, TImporto& new_diffcam) { const int nriga = new_pag.get_int(PAGSCA_NRIGA); const int nrata = new_pag.get_int(PAGSCA_NRATA); const int nrigp = new_pag.get_int(PAGSCA_NRIGP); bool empty = FALSE; if (nriga != UNASSIGNED) { TRiga_scadenze& scaden = rata(nriga, nrata); empty = scaden.modifica_pagamento(new_pag, old_ap, old_abb, old_diffcam, new_ap, new_abb, new_diffcam); } else { const TRectype& old_pag = pagamento(nriga, nrata, nrigp); TRiga_partite& sum = riga(nrigp); empty = sum.update(old_pag, new_pag, PART_IMPORTO); sum.update(old_pag, new_pag, PART_IMPORTOVAL); sum.update(old_pag, new_pag, PART_RITENUTE); if (new_pag.get_real(PAGSCA_IMPORTO).is_zero()) _unassigned.destroy_row(nrigp); else _unassigned.row(nrigp, FALSE) = new_pag; old_ap = new_ap = ' '; // Non ci possono essere abbuoni di sorta! } if (empty && !utilizzata(nrigp)) _part.destroy_row(nrigp); return empty; } bool TPartita::chiusa(bool update) { bool chiusa = FALSE; const int ultima = last(); if (ultima > 0) { const TRiga_partite& row = riga(ultima); chiusa = row.get_bool(PART_CHIUSA); } if (update) { bool forse_chiusa = TRUE; for (int p = last(); p > 0 && forse_chiusa; p = pred(p)) { const TRiga_partite& part = riga(first()); if (part.get_int(PART_TIPOMOV) == 1) { for (int r = part.last(); r > 0; r--) { const TRiga_scadenze& scad = part.rata(r); if (!scad.pagata()) { forse_chiusa = FALSE; break; } } } } if (chiusa != forse_chiusa) { chiusa = forse_chiusa; for (p = last(); p > 0; p = pred(p)) { TRiga_partite& part = riga(p); part.put(PART_CHIUSA, chiusa); } } } return chiusa; } /////////////////////////////////////////////////////////// // TPartite_array /////////////////////////////////////////////////////////// // Certified 99% const TString& TPartite_array::key(const TBill& clifo, int anno, const char* num) { if (clifo.tipo() > ' ') _key.format("%c%3d%3d%6ld%4d%s", clifo.tipo(), 0, 0, clifo.sottoconto(), anno, num); else _key.format("%c%3d%3d%6ld%4d%s", clifo.tipo(), clifo.gruppo(), clifo.conto(), clifo.sottoconto(), anno, num); return _key; } // Certified 99% TPartita* TPartite_array::find(const TBill& clifo, int anno, const char* num, bool create) { const TString& k = key(clifo, anno, num); TPartita* p = (TPartita*)objptr(k); if (p == NULL && create) { p = new TPartita(clifo, anno, num); add(k, p); } return p; } TPartita* TPartite_array::find(const TRectype& r, bool create) { TBill zio; zio.get(r); const int anno = r.get_int(PART_ANNO); const char* num = r.get_str(PART_NUMPART); return find(zio, anno, num, create); } TPartita& TPartite_array::partita(const TBill& clifo, int anno, const char* num) { TPartita* game = find(clifo, anno, num, TRUE); CHECK(game, "Partita errata"); return *game; } TPartita& TPartite_array::partita(const TRectype& r) { TPartita* game = find(r, TRUE); CHECK(game, "Partita errata"); return *game; } bool TPartite_array::destroy(const TBill& clifo, int anno, const char* num) { const TString& k = key(clifo, anno, num); return remove(k); } bool TPartite_array::write(bool re) { int err = NOERR; TPartita* game; restart(); while ((game = (TPartita*)get()) != NULL) { err = game->write(re); if (err != NOERR) // L'errore viene gia' segnalato dalla partita break; } return err == NOERR; } void TPartite_array::destroy() { TAssoc_array::destroy(); _numreg = 0; } // Aggiunge all'array tutte le partite che si riferiscono alla registrazione nreg int TPartite_array::add_numreg(long nreg) { if (nreg != _numreg) { TRelation rel(LF_PARTITE); TRectype& part = rel.lfile().curr(); // Costruzione filtro del cursore part.zero(); part.put(PART_NREG, nreg); const TRectype filter(part); TCursor cur(&rel, "", 2, &filter, &filter); for (cur = 0; cur.ok(); ++cur) partita(part); // Aggiungi partita se non esiste gia' _numreg = nreg; } return items(); } TImporto TPartite_array::importo_speso(long nreg, int numrig, bool extra) { TImporto imp; add_numreg(nreg); for (TPartita* game = first(); game; game = next()) imp += game->importo_speso(nreg, numrig, extra); return imp; } void TPartite_array::update_reg(const TRectype& mov, long old_nreg) { if (old_nreg <= 0) old_nreg = mov.get_long(MOV_NUMREG); add_numreg(old_nreg); for (TPartita* game = first(); game; game = next()) game->update_reg(old_nreg, mov); }