#include #include #include #include #include #include #include #include #include "sc1400.h" #include "../cg/cg2101.h" #include "../cg/cg2103.h" #include "../cg/cglib02.h" #include "../cg/cgsaldac.h" #include /////////////////////////////////////////////////////////// // TAbbuona_sheet /////////////////////////////////////////////////////////// enum { C_CHECKED, C_GRUPPO, C_CONTO, C_CODCF, C_RAGSOC, C_IMPORTO, C_RESIDUO, C_CODVAL, C_DATASCAD, C_ANNO, C_PARTITA, C_RIGA, C_RATA }; class TAbbuona_sheet : public TArray_sheet { protected: virtual bool get_cell_colors(int row, int col, COLOR& fore, COLOR& back) const; public: TAbbuona_sheet(); }; bool TAbbuona_sheet::get_cell_colors(int /*row*/, int col, COLOR& fore, COLOR& back) const { if (col == C_RESIDUO) // Evidenzia la colonna del residuo { fore = NORMAL_COLOR; back = REQUIRED_BACK_COLOR; return true; } return false; } TAbbuona_sheet::TAbbuona_sheet() : TArray_sheet(0, 0, 0, 0, TR("Scadenze aperte"), HR("@1C|Grp.@3|Cnt.@3|Codice@R|Ragione Sociale@50|Importo\nRata@10R|Residuo\nRata@10R|Val.|Data@10|Anno@R|Partita@8R|Riga@R|Rata@R|Non\nass.@3C"), 0x1, 1) {} /////////////////////////////////////////////////////////// // TAbbuona_mask /////////////////////////////////////////////////////////// class TAbbuona_mask : public TAutomask { protected: virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); void auto_proponi(short id, bool last); // Propone il primo/ultimo record del cursore associato al campo id public: TAbbuona_mask() : TAutomask("sc1400a") {} }; void TAbbuona_mask::auto_proponi(short id, bool last) { TEdit_field& e =efield(id); TBrowse& b = *e.browse(); TCursor& c = *b.cursor(); b.do_input(true); // Reimposta filtri const TRecnotype items = c.items(); if (items > 0) { c = last ? (items-1): 0L; // Si posiziona sul primo buono if (c.ok()) { b.do_output(); // Lo propone automaticamente e.set_dirty(); } } } bool TAbbuona_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) { switch (o.dlg()) { case F_TIPOCF: case F_INVALUTA: if (e == fe_modify) auto_proponi(F_CAUSALE, false); break; case F_CODVAL: if (e == fe_modify) { if (is_true_value(o.get())) auto_proponi(F_DATACAMBIO, true); else set(F_INVALUTA, "", 0x1); } break; case F_IMPORTO: if (e == fe_init && o.empty()) o.set(50); break; case F_CAUSALE: if (e == fe_init) auto_proponi(F_CAUSALE, false); if (e == fe_modify || e == fe_close) { const TCausale caus(o.get()); const int riga_abb = get(F_TIPOCF) == "C" ? RIGA_ABBUONI_PASSIVI : RIGA_ABBUONI_ATTIVI; TBill conto_abbuoni; caus.bill(riga_abb, conto_abbuoni); if (!conto_abbuoni.find()) return error_box(TR("Il conto per gli abbuoni alla riga %d della causale non è valido"), riga_abb); if (get_bool(F_INVALUTA)) { TBill conto_diffcam; caus.bill(RIGA_DIFFCAM, conto_diffcam); if (!conto_diffcam.find()) return error_box(TR("Il conto per le differenze cambi della causale non è valido")); } } break; case F_DATACOMP: if (e == fe_close) { const TDate datareg = get(F_DATAREG); TDate datacomp = o.get(); if (!datacomp.ok() || datacomp > datareg) { datacomp = datareg; o.set(datacomp.string()); } } break; default: break; } return true; } /////////////////////////////////////////////////////////// // TAbbuona_partite /////////////////////////////////////////////////////////// class TAbbuona_partite : public TSkeleton_application { protected: // Applicat virtual void main_loop(); bool genera_abbuono(TPartita& game, int nriga, int nrata, const TMovimentoPN& mov, TLog_report& log) const; TImporto residuo(const TRiga_scadenze& rata, bool in_valuta) const; int non_assegnati(const TPartita& part) const; public: void fill_sheet(const char tipocf, const TCurrency& impmin, TAbbuona_sheet& sheet); bool elabora(const TMask& msk, TAbbuona_sheet& sheet) const; }; TImporto TAbbuona_partite::residuo(const TRiga_scadenze& rata, bool in_valuta) const { TImporto res; if (!rata.chiusa()) { // Controllo se è una sola rata int rate_aperte = 0; const TPartita& part = rata.partita(); for (int r = part.last(); r > 0 && rate_aperte <= 0; r--) { const TRiga_partite& rp = part.riga(r); if (rp.tipo() == tm_fattura) { for (int s = rp.rate(); s > 0; s--) if (!rp.rata(s).chiusa()) rate_aperte++; } } if (rate_aperte == 1) res = part.calcola_saldo(in_valuta); else res = rata.residuo(in_valuta); const char tipocf = rata.get_char(SCAD_TIPOCF); res.normalize(tipocf == 'C' ? 'D' : 'A'); } return res; } int TAbbuona_partite::non_assegnati(const TPartita& part) const { const char tipocf = part.conto().tipo(); int na = ((TPartita&)part).unassigned().rows(); for (int r = part.last(); r > 0; r = part.pred(r)) { const TRiga_partite& riga = part.riga(r); if (riga.tipo() == tm_fattura) { TImporto imp = riga.importo(true); imp.normalize(tipocf == 'C' ? 'D' : 'A'); if (imp.valore() < ZERO) na++; } } return na; } void TAbbuona_partite::fill_sheet(const char tipocf, const TCurrency& imp_min, TAbbuona_sheet& sheet) { const bool in_valuta = !imp_min.is_firm_value(); const TString4 cod_val = in_valuta ? imp_min.get_value() : EMPTY_STRING; const TCurrency zero_val(ZERO, cod_val); TString query; // Selezione delle scadenze aperte query << "USE SCAD\n" << "SELECT (PAGATA!=\"X\")&&(BLOCCATA!=\"X\")\n" << "FROM TIPOC=" << tipocf << "\nTO TIPOC=" << tipocf; TISAM_recordset scadenze(query); const TRectype& scad = scadenze.cursor()->curr(); TString msg; msg << TR("Elaborazione scadenze aperte per meno di ") << imp_min.string(true) << ' '; msg << (in_valuta ? cod_val : TCurrency::get_firm_val()); TProgress_monitor pi(scadenze.items(), msg); TToken_string row; // Riga di lavoro sullo sheet for (bool ok = scadenze.move_first(); ok; ok = scadenze.move_next()) { if (!pi.add_status(1)) break; row.cut(0) << tipocf; row.add(0); row.add(0); row.add(scad.get(SCAD_SOTTOCONTO)); row.add(scad.get(SCAD_ANNO)); row.add(scad.get(SCAD_NUMPART)); row.add(scad.get(SCAD_NRIGA)); const TRectype& game_rec = cache().get(LF_PARTITE, row); if (game_rec.empty()) continue; // Ignora partite con la valuta incompatibile const TString& game_val = game_rec.get(PART_CODVAL); if (in_valuta) { if (!same_values(game_val, cod_val)) continue; } else { if (is_true_value(game_val)) continue; } TPartita partita(game_rec); const int nriga = scad.get_int(SCAD_NRIGA); const int nrata = scad.get_int(SCAD_NRATA); if (!partita.esiste(nriga, nrata)) continue; const TRiga_scadenze& rata = partita.rata(nriga, nrata); TImporto imp_rat = rata.importo(in_valuta); imp_rat.normalize(tipocf == 'C' ? 'D' : 'A'); if (imp_rat.valore() < ZERO) continue; // Nota di credito const TImporto imp_res = residuo(rata, in_valuta); const TCurrency residuo(imp_res.valore(), cod_val); // if (residuo > zero_val && residuo <= imp_min) // Vecchio modo (solo positivo) if (!residuo.is_zero() && abs(residuo.get_num()) < imp_min.get_num()) // Nuovo modo (anche negativi) { TImporto imp_val = rata.importo(in_valuta); imp_val.normalize(imp_res.sezione()); const TCurrency importo(imp_val.valore(), cod_val); const TBill& conto = partita.conto(); row = "X"; row.add(game_rec.get(PART_GRUPPOCL)); row.add(game_rec.get(PART_CONTOCL)); row.add(conto.codclifo()); row.add(conto.descrizione()); row.add(importo.string(true)); row.add(residuo.string(true)); row.add(cod_val); row.add(scad.get(SCAD_DATASCAD)); row.add(scad.get(SCAD_ANNO)); row.add(scad.get(SCAD_NUMPART)); row.add(nriga); row.add(nrata); row.add(non_assegnati(partita) ? "X" : ""); sheet.add(row); } } } bool TAbbuona_partite::genera_abbuono(TPartita& game, int nriga, int nrata, const TMovimentoPN& mov, TLog_report& log) const { TString msg; msg.format(FR(" %d della riga %d della partita %d/%s"), nrata, nriga, game.anno(), (const char*)game.numero()); if (!game.esiste(nriga, nrata)) // Non si sa mai col saldaconto! { msg.insert(TR("Non esiste la rata")); log.log(1, msg); return false; } const TRectype& head = mov.curr(); const TValuta valuta(head); // Creo una riga di partita di tipo pagamento e ci copio i dati della testata del movimento TRiga_partite& riga = game.new_row(); const int nrigp = riga.get_int(PART_NRIGA); riga.put(PART_TIPOMOV, tm_pagamento); riga.put(PART_SEZ, game.conto().tipo() == 'F' ? 'D' : 'A'); riga.put(PART_NREG, head.get(MOV_NUMREG)); riga.put(PART_NUMRIG, mov.cg_items()); riga.put(PART_DATAREG, head.get(MOV_DATAREG)); riga.put(PART_DATAPAG, head.get(MOV_DATACOMP)); riga.put(PART_CODCAUS, head.get(MOV_CODCAUS)); riga.put(PART_NUMDOC, head.get(MOV_NUMDOC)); riga.put(PART_DATADOC, head.get(MOV_DATADOC)); riga.put(PART_DESCR, head.get(MOV_DESCR)); if (valuta.in_valuta()) valuta.put(riga); // Creo una nuova riga di pagamento assegnado il flag di saldo TRiga_scadenze& scad = game.rata(nriga, nrata); TRectype new_pag = scad.new_row(nrigp); // Creo nuova riga e la duplico const bool unass = non_assegnati(game) > 0; if (unass) // Pago la differenza ... e non mi fido { const TRiga_scadenze& r = game.rata(nriga, nrata); TImporto res = residuo(r, false); new_pag.put(PAGSCA_IMPORTO, res.valore()); if (valuta.in_valuta()) { res = residuo(r, true); new_pag.put(PAGSCA_IMPORTOVAL, res.valore()); } } else new_pag.put(PAGSCA_ACCSAL, 'S'); // Pongo pagamento a saldo ... e mi fido const bool empty = game.modifica_pagamento(new_pag, valuta, true); if (empty) { msg.insert(TR("Errore di chiusura della rata")); log.log(2, msg); } else { if (unass) msg.insert(TR("Chiusa partita con rata")); else msg.insert(TR("Chiusa singola rata")); log.log(0, msg); } return !empty; } bool TAbbuona_partite::elabora(const TMask& msk, TAbbuona_sheet& scad_sheet) const { // La validità di tutti questi campi è già garantita dai controlli di msk const char tipo_cf = msk.get(F_TIPOCF)[0]; const TDate datareg = msk.get(F_DATAREG); TDate datacomp = msk.get(F_DATACOMP); if (!datacomp.ok()) datacomp = datareg; const TValuta valuta(msk, F_CODVAL, F_DATACAMBIO, F_CAMBIO); TEsercizi_contabili esc; const int annoes = esc.date2esc(datacomp); const TCausale caus(msk.get(F_CAUSALE), datareg.year()); const int riga_abb = tipo_cf == 'C' ? RIGA_ABBUONI_PASSIVI : RIGA_ABBUONI_ATTIVI; TBill conto_abbuoni; caus.bill(riga_abb, conto_abbuoni); TBill conto_diffcam; if (valuta.in_valuta()) caus.bill(RIGA_DIFFCAM, conto_diffcam); TMovimentoPN mov; TRectype& head = mov.curr(); head.put(MOV_CODCAUS, msk.get(F_CAUSALE)); head.put(MOV_TIPOMOV, tm_pagamento); head.put(MOV_DATAREG, datareg); head.put(MOV_DATACOMP, datacomp); head.put(MOV_ANNOIVA, datareg.year()); head.put(MOV_ANNOES, annoes); head.put(MOV_DESCR, TR("Abbuoni automatici")); if (valuta.in_valuta()) valuta.put(head); if (mov.write() != NOERR) return error_box(TR("Impossibile creare il movimento di prima nota")); const long numreg = head.get_long(MOV_NUMREG); // Numero registrazione per saldaconto TLog_report log; TString msg; msg << TR("Creazione della testata del movimento n.") << numreg; log.log(1, msg); TPartite_array partite; bool can_write = true; TBill last_clifo; FOR_EACH_CHECKED_ROW(scad_sheet, r, row) { const TBill bill(row->get_int(C_GRUPPO), row->get_int(C_CONTO), row->get_long(C_CODCF), tipo_cf); const int anno = row->get_int(C_ANNO); const TString8 numpart = row->get(C_PARTITA); const int nriga = row->get_int(C_RIGA); const int nrata = row->get_int(C_RATA); TPartita& game = partite.partita(bill, anno, numpart); if (bill != last_clifo) { log.log(0, msg.cut(0)); msg << TR("Elaborazione scadenze ") << bill.descrizione(); log.log(1, msg); TRectype& new_rmov = mov.cg(-1); bill.put(new_rmov); new_rmov.put(RMV_ROWTYPE, 'K'); new_rmov.put(RMV_ANNOES, annoes); new_rmov.put(RMV_DATAREG, datareg); msg = TR("Saldo partita "); msg << game.anno() << '/' << game.numero(); new_rmov.put(RMV_DESCR, msg); last_clifo = bill; } if (!genera_abbuono(game, nriga, nrata, mov, log)) can_write = false; } TImporto banca, abbuoni, diffcam; for (int i = 0; i < mov.cg_items(); i++) { // Calcola importo speso sul saldaconto per ogni riga contabile const TImporto abb = partite.importo_speso(numreg, i+1, false, 0x2); abbuoni -= abb; // Aggiungi abbuono al relativo totalizzatore TImporto dic; if (valuta.in_valuta()) { dic = partite.importo_speso(numreg, i+1, false, 0x4); diffcam -= dic; // Aggiungi differenza cambi al relativo totalizzatore } const TImporto imp = partite.importo_speso(numreg, i+1); banca -= imp; banca += abb; banca += dic; TRectype& rmov = mov.cg(i); rmov.put(RMV_SEZIONE, imp.sezione()); rmov.put(RMV_IMPORTO, imp.valore()); rmov.put(RMV_ANNOES, annoes); rmov.put(RMV_DATAREG, datareg); } if (!banca.is_zero()) { banca.normalize(); TRectype& rmov = mov.cg(-1); rmov.put(RMV_ROWTYPE, 'I'); // Importi rmov.put(RMV_SEZIONE, banca.sezione()); rmov.put(RMV_IMPORTO, banca.valore()); rmov.put(RMV_ANNOES, annoes); rmov.put(RMV_DATAREG, datareg); conto_abbuoni.put(rmov); mov.curr().add(MOV_TOTDOC, -banca.valore()); } if (!abbuoni.is_zero()) { abbuoni.normalize(); TRectype& rmov = mov.cg(-1); rmov.put(RMV_ROWTYPE, tipo_cf == 'F' ? 'A' : 'P'); // Abbuoni attivi o passivi? rmov.put(RMV_SEZIONE, abbuoni.sezione()); rmov.put(RMV_IMPORTO, abbuoni.valore()); rmov.put(RMV_ANNOES, annoes); rmov.put(RMV_DATAREG, datareg); conto_abbuoni.put(rmov); } if (!diffcam.is_zero()) { diffcam.normalize(); TRectype& rmov = mov.cg(-1); rmov.put(RMV_ROWTYPE, 'C'); // Differenze cambio rmov.put(RMV_SEZIONE, diffcam.sezione()); rmov.put(RMV_IMPORTO, diffcam.valore()); rmov.put(RMV_ANNOES, annoes); rmov.put(RMV_DATAREG, datareg); conto_diffcam.put(rmov); } if (can_write) { if (mov.rewrite() == NOERR) { log.log(0, msg.cut(0)); if (partite.write(true)) { msg << TR("Aggiornato saldaconto legato al movimento n.") << numreg; log.log(1, msg); } else { msg << TR("Errore in aggiornamento saldaconto legato al movimento n.") << numreg; log.log(2, msg); can_write = false; } TSaldo_agg sa; for (int i = 0; i < mov.cg_items(); i++) { const TRectype& rmov = mov.cg(i); sa.aggiorna(rmov); } sa.registra(); } else { log.log(2, "Impossibile aggiornare il movimento"); can_write = false; } } if (!can_write) { const int err = mov.remove(); msg = err == NOERR ? TR("Eliminato") : TR("Impossibile eliminare"); msg << TR(" il movimento n.") << numreg; log.log(1, msg); } log.preview(); return can_write; } void TAbbuona_partite::main_loop() { TAbbuona_mask msk; while (msk.run() == K_ENTER) { const char tipo_cf = msk.get(F_TIPOCF)[0]; TCurrency imp_min; msk.get_currency(F_IMPORTO, imp_min); TAbbuona_sheet scad_sheet; fill_sheet(tipo_cf, imp_min, scad_sheet); if (scad_sheet.run() == K_ENTER && scad_sheet.one_checked()) elabora(msk, scad_sheet); } } int sc1400(int argc, char** argv) { TAbbuona_partite a; a.run(argc, argv, TR("Abbuoni automatici")); return 0; }