Files correlati : Ricompilazione Demo : [ ] Commento : Riportata la versione 2.1 300 git-svn-id: svn://10.65.10.50/trunk@13091 c028cbd2-c16b-5b4b-a496-9718f37d4682
1130 lines
30 KiB
C++
Executable File
1130 lines
30 KiB
C++
Executable File
// Applicazione di generazione reports & stampe per MRP
|
|
#include <applicat.h>
|
|
#include <form.h>
|
|
#include <relation.h>
|
|
#include <reprint.h>
|
|
#include <printer.h>
|
|
#include <progind.h>
|
|
|
|
#include "mrplib.h"
|
|
#include "../ve/velib.h"
|
|
#include "../mg/mglib.h"
|
|
|
|
#include "mr1.h"
|
|
#include "mr1100a.h"
|
|
#include <defmask.h>
|
|
|
|
#include <doc.h>
|
|
#include <rdoc.h>
|
|
|
|
#define LF_MRPREP 132
|
|
|
|
#define LAST_BUCKET 12
|
|
|
|
// Tipi di stampa
|
|
enum tipo_stampa {
|
|
articoli, // produzione articoli nel tempo
|
|
linee, // carico linee
|
|
scheduling, // scheduling delle linee
|
|
available // Tipo di stampa non definita: ordinata per articoli e stampa del carico della linea
|
|
};
|
|
|
|
// I tipi di stampa si identificano secondo la seguente tabella, tramite l'intersezione
|
|
// tra l'ordinamento ed il valore impostati:
|
|
/*
|
|
|
|
|----------------------+
|
|
|quantita | carico |
|
|
-----------+-----------------------
|
|
articoli |articoli | available|
|
|
-----------+----------------------+
|
|
linea |scheduling | linee |
|
|
-----------+-----------------------
|
|
|
|
*/
|
|
enum tipo_ordinamento {
|
|
articolo,
|
|
linea
|
|
};
|
|
|
|
enum tipo_valore {
|
|
quantita,
|
|
carico
|
|
};
|
|
|
|
enum tipo_dettaglio {
|
|
det_articolo,
|
|
det_giacenza,
|
|
det_impianto,
|
|
det_linea
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// _TCapacitaLinea, usato per calcolare le capacita' delle linee
|
|
///////////////////////////////////////////////////////////
|
|
|
|
class _TCapacitaLinea : public TObject
|
|
{
|
|
TArray _cap_min, // Array 1..LAST_BUCKET-1 di capacita' minime
|
|
_cap_max; // Array 1..LAST_BUCKET-1 di capacita' massime
|
|
|
|
public:
|
|
TArray& cap_min() { return _cap_min; }
|
|
TArray& cap_max() { return _cap_max; }
|
|
_TCapacitaLinea() ;
|
|
virtual ~_TCapacitaLinea() {}
|
|
};
|
|
|
|
_TCapacitaLinea::_TCapacitaLinea()
|
|
{
|
|
for (int i = 0; i < LAST_BUCKET-1; i++)
|
|
{
|
|
_cap_min.add(new real);
|
|
_cap_max.add(new real);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TMRP_rep_record
|
|
///////////////////////////////////////////////////////////
|
|
|
|
class TMRP_rep_record : public TObject
|
|
{
|
|
public:
|
|
real _qta;
|
|
|
|
TMRP_rep_record() { }
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TMRP_rep_record_array;
|
|
///////////////////////////////////////////////////////////
|
|
|
|
class TMRP_rep_record_array : public TObject
|
|
{
|
|
TArray _buckets;
|
|
|
|
public:
|
|
int items() const { return _buckets.items(); }
|
|
int last() const { return _buckets.last(); }
|
|
int pred(int i) const { return _buckets.pred(i); }
|
|
|
|
TMRP_rep_record& operator[](int b);
|
|
};
|
|
|
|
TMRP_rep_record& TMRP_rep_record_array::operator[](int b)
|
|
{
|
|
CHECKD(b >= 0, "Invalid TMRP_rep_record_array ", b);
|
|
TMRP_rep_record* qta = (TMRP_rep_record*)_buckets.objptr(b);
|
|
if (qta == NULL)
|
|
{
|
|
qta = new TMRP_rep_record;
|
|
_buckets.add(qta, b);
|
|
}
|
|
return *qta;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TMRP_rep_line
|
|
///////////////////////////////////////////////////////////
|
|
|
|
class TMRP_rep_line : public TSortable
|
|
{
|
|
TCodice_articolo _codart;
|
|
TString16 _giac;
|
|
TString8 _imp, _lin;
|
|
TString4 _um;
|
|
|
|
TMRP_rep_record_array _bucket;
|
|
|
|
protected:
|
|
virtual int compare(const TSortable& s) const;
|
|
|
|
public:
|
|
const TCodice_articolo& codice() const { return _codart; }
|
|
const TString& livgiac() const { return _giac; }
|
|
const TString& codimp() const { return _imp; }
|
|
const TString& codlin() const { return _lin; }
|
|
const TString& um() const { return _um; }
|
|
|
|
int last() const { return _bucket.last(); }
|
|
int pred(int i) const { return _bucket.pred(i); }
|
|
|
|
real& qta(int b) { return _bucket[b]._qta; }
|
|
|
|
void fill(TRectype& record, TAssoc_array* capacita = NULL);
|
|
|
|
TMRP_rep_line(const TCodice_articolo& codart,
|
|
const TString& giac, const TString& imp,
|
|
const TString& lin, const TString& um);
|
|
virtual ~TMRP_rep_line() { }
|
|
};
|
|
|
|
void TMRP_rep_line::fill(TRectype& record, TAssoc_array* capacita)
|
|
{
|
|
record.zero();
|
|
record.put("TIPO", "R");
|
|
record.put("CODART", _codart);
|
|
record.put("LIVELLO", _giac);
|
|
record.put("IMPIANTO", _imp);
|
|
record.put("LINEA", _lin);
|
|
record.put("UM", _um);
|
|
TString16 campo;
|
|
for (int b = last(); b >= 0; b = pred(b))
|
|
{
|
|
switch(b)
|
|
{
|
|
case 0:
|
|
campo = "QTAFIRST"; break;
|
|
case LAST_BUCKET:
|
|
campo = "QTALAST"; break;
|
|
default:
|
|
campo.format("QTA%d", b); break;
|
|
}
|
|
record.put(campo, qta(b));
|
|
}
|
|
|
|
if (capacita)
|
|
for (b=1; b<LAST_BUCKET; b++)
|
|
{
|
|
_TCapacitaLinea* cl = (_TCapacitaLinea*)capacita->objptr(_lin);
|
|
if (cl)
|
|
{
|
|
TArray& cmin = cl->cap_min();
|
|
TArray& cmax = cl->cap_max();
|
|
campo.format("CMIN%d", b);
|
|
record.put(campo, (real&)cmin[b-1]); // Capacita' minima...
|
|
campo.format("CMAX%d", b);
|
|
record.put(campo, (real&)cmax[b-1]); // e massima
|
|
}
|
|
}
|
|
}
|
|
|
|
int TMRP_rep_line::compare(const TSortable& s) const
|
|
{
|
|
const TMRP_rep_line& c = (const TMRP_rep_line&)s;
|
|
int cmp = _codart.compare(c._codart);
|
|
if (cmp == 0)
|
|
{
|
|
cmp = _giac.compare(c._giac);
|
|
if (cmp == 0)
|
|
{
|
|
cmp = _imp.compare(c._imp);
|
|
if (cmp == 0)
|
|
{
|
|
cmp = _lin.compare(c._lin);
|
|
if (cmp == 0)
|
|
cmp = _um.compare(c._um);
|
|
}
|
|
}
|
|
}
|
|
return cmp;
|
|
}
|
|
|
|
TMRP_rep_line::TMRP_rep_line(const TCodice_articolo& codart,
|
|
const TString& giac,
|
|
const TString& imp, const TString& lin,
|
|
const TString& um)
|
|
: _codart(codart), _giac(giac), _imp(imp), _lin(lin), _um(um)
|
|
{ }
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TMRP_rep_lines
|
|
///////////////////////////////////////////////////////////
|
|
|
|
class TMRP_rep_lines : public TMRP_array
|
|
{
|
|
protected:
|
|
virtual TSortable* new_obj(const TToken_string& key) const;
|
|
|
|
public:
|
|
TMRP_rep_line* find(const TCodice_articolo& codart,
|
|
const TString& giac, const TString& imp,
|
|
const TString& lin, const TString& um,
|
|
bool create);
|
|
TMRP_rep_line& operator[](long n) const
|
|
{ return (TMRP_rep_line&)find_obj(n); }
|
|
};
|
|
|
|
TSortable* TMRP_rep_lines::new_obj(const TToken_string& key) const
|
|
{
|
|
TCodice_articolo art; key.get(0, art);
|
|
TString gia; key.get(1, gia);
|
|
TString imp; key.get(2, imp);
|
|
TString lin; key.get(3, lin);
|
|
TString um ; key.get(4, um);
|
|
return new TMRP_rep_line(art, gia, imp, lin, um);
|
|
}
|
|
|
|
TMRP_rep_line* TMRP_rep_lines::find(const TCodice_articolo& codart,
|
|
const TString& giac,
|
|
const TString& imp,
|
|
const TString& lin,
|
|
const TString& um,
|
|
bool create)
|
|
{
|
|
_key = codart;
|
|
_key.add(giac);
|
|
_key.add(imp);
|
|
_key.add(lin);
|
|
_key.add(um);
|
|
TSortable* s = create ? add_obj(_key) : find_obj(_key);
|
|
return (TMRP_rep_line*)s;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// MRP report generator mask
|
|
///////////////////////////////////////////////////////////
|
|
class TRepgen_mask : public TCalendar_mask
|
|
{
|
|
protected:
|
|
void calcola_capacita(TAssoc_array&) const ;
|
|
bool test_tipodoc_num( const TSheet_field &sheet_num , const TSheet_field &sheet_type) ;
|
|
virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);
|
|
|
|
void round_field(TMask_field& fld, bool up) const;
|
|
|
|
public:
|
|
int round_date(TDate& date, bool up) const;
|
|
int get_bucket_size() const { return get_int(F_BUCKET) * get_int(F_DAYXBUCK);}
|
|
const TDate start_date() const { return get_date(F_DADATA);}
|
|
const TDate end_date() const { return get_date(F_ADATA);}
|
|
int last_bucket() { TDate d(end_date()); return round_date(d, true);}
|
|
bool elabora() const;
|
|
TRepgen_mask();
|
|
virtual ~TRepgen_mask() { }
|
|
};
|
|
|
|
bool TRepgen_mask::test_tipodoc_num(const TSheet_field &sheet_num ,const TSheet_field &sheet_type)
|
|
{
|
|
TString16 tipo;
|
|
TString_array& nums = sheet_num.rows_array();
|
|
TString_array& types = sheet_type.rows_array();
|
|
for (int j = types.items()-1; j >= 0; j--)
|
|
{
|
|
bool ok=FALSE;
|
|
tipo = types.row(j).get(0) ;
|
|
for (int i = nums.items()-1; i >= 0; i--)
|
|
{
|
|
TCodice_numerazione num(nums.row(i).get(0));
|
|
for (int n = num.ntipi_doc()-1;n >= 0; n--)
|
|
{
|
|
const char* t = num.tipo_doc(n);
|
|
if (tipo == t)
|
|
ok = TRUE;
|
|
}
|
|
}
|
|
if (!ok)
|
|
return error_box(FR("Il tipo '%s' non appartiene a nessuna delle numerazioni scelte"),(const char*)tipo);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
int TRepgen_mask::round_date(TDate& date, bool up) const
|
|
{
|
|
// Dimensione del bucket in giorni
|
|
const int bucket_size = get_bucket_size();
|
|
|
|
TDate inizio(start_date());
|
|
int bucket;
|
|
if (bucket_size == 31) // mese solare
|
|
{
|
|
if (up)
|
|
{
|
|
date.set_end_month();
|
|
int wday = date.wday();
|
|
wday = (7-wday) % 7;
|
|
if (wday<0)
|
|
date += wday;
|
|
}
|
|
else
|
|
date.set_day(1);
|
|
bucket = (date.year()-inizio.year())*12 + date.month() - inizio.month();
|
|
if (bucket < 0)
|
|
bucket = 0;
|
|
}
|
|
else
|
|
{
|
|
if (bucket_size > 1) // non vado a giorni
|
|
{
|
|
const int wday = inizio.wday();
|
|
if (wday > 1) inizio -= wday-1;
|
|
}
|
|
// Calcola il bucket di appartenenza
|
|
const int days = int(date - inizio);
|
|
|
|
bucket = days / bucket_size;
|
|
if (bucket < 0)
|
|
bucket = 0;
|
|
else
|
|
if (bucket > LAST_BUCKET)
|
|
bucket = LAST_BUCKET;
|
|
if (up) // Arrotonda alla fine del bucket
|
|
date = inizio + long((bucket+1 )* bucket_size - 1 /*- get_int(F_LASTWRKDAY)*/);
|
|
else // Arrotonda all'inizio del bucket
|
|
date = inizio + long(bucket * bucket_size);
|
|
}
|
|
return bucket;
|
|
}
|
|
|
|
void TRepgen_mask::round_field(TMask_field& fld, bool up) const
|
|
{
|
|
TDate date = fld.get();
|
|
if (date.ok())
|
|
{
|
|
round_date(date, up);
|
|
fld.set(date);
|
|
}
|
|
}
|
|
|
|
void TRepgen_mask::calcola_capacita(TAssoc_array& capacita) const
|
|
{
|
|
if (capacita.items() == 0)
|
|
return;
|
|
|
|
const bool carico_uomo = get_bool(F_MANLOAD);
|
|
const TDate df(start_date());
|
|
const TDate dt(end_date());
|
|
TDate wd1,wd2;
|
|
TString16 codimp;
|
|
|
|
capacita.restart();
|
|
TProgind pi(capacita.items(), TR("Calcolo capacita linee"), FALSE, TRUE);
|
|
THash_object* ho = capacita.get_hashobj();
|
|
for (;ho != NULL; ho = capacita.get_hashobj())
|
|
{
|
|
pi.addstatus(1L);
|
|
const TString& linea = ho->key();
|
|
_TCapacitaLinea& cl = (_TCapacitaLinea&) ho->obj();
|
|
TLinea_prod lp(linea);
|
|
codimp = lp.codimp();
|
|
|
|
TMRP_calendar& mc = TMRP_time::get_calendar(codimp, linea);
|
|
TArray& cap_min = cl.cap_min();
|
|
TArray& cap_max = cl.cap_max();
|
|
|
|
for (wd1 = df; wd1 <= dt; ++wd1) // Piglia tutte le date nel range! (very, very heavy...)
|
|
{
|
|
wd2 = wd1;
|
|
const int bucket = round_date(wd2, FALSE); // da 0 a 10 al massimo
|
|
|
|
if (bucket > 10)
|
|
break; // Fine
|
|
real & v1 = (real&) cap_min[bucket];
|
|
v1 += carico_uomo ? mc.add_oreuomo(v1, wd1) : mc.add_oremacchina(v1, wd1);
|
|
real & v2 = (real&) cap_max[bucket];
|
|
v2 += carico_uomo ? mc.add_oreuomo_max(v2, wd1) : mc.add_oremacchina_max(v2, wd1);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool TRepgen_mask::elabora() const
|
|
{
|
|
TRelation rel(LF_RIGHEDOC);
|
|
TCursor cur(&rel);
|
|
const TRectype& riga = cur.curr();
|
|
TRectype filter_fr(riga), filter_to(riga);
|
|
|
|
const TDate date_fr(start_date());
|
|
const int year_fr = date_fr.year();
|
|
TDate date_to(end_date());
|
|
const int year_to = date_to.year();
|
|
const int bucket_size = get_bucket_size();
|
|
const TExplosion_grouping raggr = (TExplosion_grouping)get_int(F_RAGGRUM);
|
|
const tipo_valore ts = (tipo_valore) get_int(F_VAL2PRINT);
|
|
const bool carico_uomo = get_bool(F_MANLOAD);
|
|
|
|
const int last_bucket = round_date(date_to,TRUE)+1;
|
|
|
|
TString16 ws;
|
|
|
|
TDistinta_tree distinta;
|
|
TArray lav_array;
|
|
|
|
TMRP_rep_lines articles;
|
|
TAssoc_array capacita;
|
|
|
|
TString msg;
|
|
TSheet_field& numerazioni = sfield(F_NUMERAZIONI);
|
|
FOR_EACH_SHEET_ROW(numerazioni, r, row)
|
|
{
|
|
const TString16 codnum = row->get(0);
|
|
// Filtra il cursore in modo da limitarlo alla numerazione
|
|
// corrente ed agli anni specificati dalle due date limite
|
|
filter_fr.put(RDOC_PROVV, "D");
|
|
filter_fr.put(RDOC_CODNUM, codnum);
|
|
filter_fr.put(RDOC_ANNO, year_fr);
|
|
|
|
filter_to.put(RDOC_PROVV, "D");
|
|
filter_to.put(RDOC_CODNUM, codnum);
|
|
filter_to.put(RDOC_ANNO, year_to);
|
|
|
|
cur.setregion(filter_fr, filter_to);
|
|
const long items = cur.items();
|
|
cur.freeze(TRUE);
|
|
|
|
msg = TR("Elaborazione numerazione "); msg << codnum;
|
|
TProgind pi(items, msg, FALSE, TRUE);
|
|
|
|
// Scandisce le righe dei documenti
|
|
for (cur = 0; cur.pos() < items; ++cur)
|
|
{
|
|
pi.addstatus(1);
|
|
const TCodice_articolo art = riga.get(RDOC_CODARTMAG);
|
|
if (art.not_empty())
|
|
{
|
|
real qta;
|
|
if (!riga.get_bool(RDOC_RIGAEVASA))
|
|
{
|
|
qta = riga.get_real(RDOC_QTA);
|
|
qta -= riga.get_real(RDOC_QTAEVASA);
|
|
}
|
|
// Seleziona le righe articolo non ancora evase
|
|
if (qta > ZERO)
|
|
{
|
|
const TString16 liv = riga.get(RDOC_LIVELLO);
|
|
const TString16 imp = riga.get(RDOC_IMPIANTO);
|
|
const TString16 lin = riga.get(RDOC_LINEA);
|
|
const TCodice_um um = riga.get(RDOC_UMQTA);
|
|
TQuantita q(art, um, qta);
|
|
TDate datacons = riga.get(RDOC_DATACONS);
|
|
|
|
int bucket = round_date(datacons, FALSE);
|
|
if (bucket < 0)
|
|
bucket = 0;
|
|
if (bucket > last_bucket)
|
|
bucket = last_bucket;
|
|
|
|
// Calcoli per carico
|
|
if (ts == carico)
|
|
{
|
|
distinta.set_root(riga);
|
|
real ore,tot;
|
|
|
|
TRiga_esplosione * llav = distinta.first_labor(lav_array, raggr);
|
|
TLavorazione * lavorazione = TDistinta_tree::find_labor(llav);
|
|
|
|
while (llav)
|
|
{
|
|
const int linea = lavorazione->find_linea(lin);
|
|
|
|
const real prod_linea = lavorazione->produttiv_linea(linea);
|
|
ore = (llav->val() * lavorazione->um_temporale().converti_in_ore()) / prod_linea;
|
|
if (carico_uomo)
|
|
ore *= lavorazione->numpers_linea(linea);
|
|
|
|
//rep.qta(bucket) += ore;
|
|
tot += ore;
|
|
llav = distinta.next_labor(lav_array);
|
|
}
|
|
|
|
if (tot > ZERO)
|
|
{
|
|
TMRP_rep_line& rep = *articles.find(art, liv, imp, lin, ws, TRUE);
|
|
rep.qta(bucket) += tot;
|
|
}
|
|
|
|
|
|
// Sbatte nella cache le linee di cui dopo calcolera' le capacita' minime e massime
|
|
// In modo che il calcolo venga effettuato una volta sola per ogni linea.
|
|
if (capacita.objptr(lin) == NULL)
|
|
capacita.add(lin, new _TCapacitaLinea); // Vuoto per ora... lo riempie poco piu' giu'
|
|
}
|
|
else // Produzione articoli nel tempo & scheduling linee
|
|
{
|
|
TMRP_rep_line& rep = *articles.find(art, liv, imp, lin, q.um(), TRUE);
|
|
switch (raggr)
|
|
{
|
|
case RAGGR_EXP_UMBASE: q.convert2umbase(); break;
|
|
case RAGGR_EXP_UMDIST: q.convert2umdist(); break;
|
|
default: break;
|
|
}
|
|
rep.qta(bucket) += q.val();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
cur.freeze(FALSE);
|
|
}
|
|
|
|
if (ts == carico && get_bool(F_CAPACITA))
|
|
calcola_capacita(capacita);
|
|
|
|
const long total = articles.items();
|
|
TProgind pi(total, TR("Generazione file"), FALSE, TRUE);
|
|
|
|
TMask_field& fld = field(F_FILENAME);
|
|
|
|
TLocalisamfile* rep = NULL;
|
|
|
|
if (fld.empty())
|
|
{
|
|
TSystemisamfile mrprep(LF_MRPREP);
|
|
mrprep.zap();
|
|
|
|
rep = new TLocalisamfile(LF_MRPREP);
|
|
}
|
|
else
|
|
{
|
|
TFilename filename = fld.get();
|
|
if (filename.empty())
|
|
filename.temp("mrprep");
|
|
fld.set(filename);
|
|
filename.insert("%");
|
|
rep = new TIsamtempfile(LF_MRPREP, filename);
|
|
}
|
|
|
|
// Scrive la testata (Record tipo "H");
|
|
rep->put("TIPO","H");
|
|
rep->put("CODART", date_fr.string());
|
|
rep->put("LIVELLO", get(F_ADATA));
|
|
rep->put("IMPIANTO", bucket_size);
|
|
rep->put("LINEA", ts == quantita ? "Q" : "C");
|
|
|
|
// Scrive le date nelle quantita' dei buckets
|
|
TDate fd = date_fr;
|
|
--fd;
|
|
rep->put("QTAFIRST", fd.string(ANSI));
|
|
++fd;
|
|
for (int b = 1; b <= last_bucket; b++)
|
|
{
|
|
TString8 fldname; fldname.format("QTA%d", b);
|
|
rep->put(fldname, fd.string(ANSI));
|
|
fd += bucket_size;
|
|
}
|
|
rep->put("QTALAST", fd.string(ANSI));
|
|
|
|
rep->write();
|
|
|
|
for (long i = 0; i < total; i++)
|
|
{
|
|
pi.addstatus(1);
|
|
articles[i].fill(rep->curr(), ts == carico ? &capacita : NULL);
|
|
int err = rep->write();
|
|
if (err != NOERR)
|
|
rep->rewrite();
|
|
}
|
|
delete rep;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TRepgen_mask::on_field_event(TOperable_field& o, TField_event e, long jolly)
|
|
{
|
|
switch(o.dlg())
|
|
{
|
|
case F_DADATA:
|
|
if (e == fe_modify || e == fe_close)
|
|
round_field(o, false);
|
|
break;
|
|
case F_ADATA:
|
|
if (e == fe_modify || e == fe_close)
|
|
round_field(o, true);
|
|
break;
|
|
case F_BUCKET:
|
|
if (e == fe_modify)
|
|
{
|
|
round_field(field(F_DADATA), false);
|
|
round_field(field(F_ADATA), true);
|
|
}
|
|
break;
|
|
case F_NUMERAZIONI:
|
|
if (e == fe_init)
|
|
{
|
|
TSheet_field& s = (TSheet_field&)o;
|
|
if (s.items() == 0)
|
|
{
|
|
s.row(0);
|
|
s.force_update();
|
|
}
|
|
}
|
|
if (e == fe_close)
|
|
{
|
|
const TSheet_field& s = (const TSheet_field&)o;
|
|
FOR_EACH_SHEET_ROW_BACK(s, r, row)
|
|
if (!row->empty_items()) return TRUE;
|
|
return error_box(TR("E' necessario inserire almeno una numerazione"));
|
|
}
|
|
break;
|
|
case F_TIPI:
|
|
if (e == fe_init)
|
|
{
|
|
TSheet_field& s = (TSheet_field&)o;
|
|
if (s.items() == 0)
|
|
{
|
|
s.row(0);
|
|
s.force_update();
|
|
}
|
|
}
|
|
if (e == fe_close)
|
|
{
|
|
const TSheet_field& s = (const TSheet_field&)o;
|
|
FOR_EACH_SHEET_ROW_BACK(s, r, row)
|
|
if (!row->empty_items())
|
|
{
|
|
const bool ok=test_tipodoc_num(sfield(F_NUMERAZIONI), s );
|
|
return ok;
|
|
}
|
|
return error_box(TR("E' necessario inserire almeno una riga"));
|
|
}
|
|
break;
|
|
case F_YEAR:
|
|
case F_IMPIANTO:
|
|
case F_LINEA:
|
|
if (e == fe_modify)
|
|
update_calendar(F_CALENDAR, F_YEAR, F_IMPIANTO, F_LINEA);
|
|
break;
|
|
case DLG_PRINT:
|
|
case DLG_ELABORA:
|
|
if (e == fe_button)
|
|
{
|
|
if (check_fields())
|
|
save_profile();
|
|
if (o.dlg() == DLG_ELABORA)
|
|
elabora();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
TRepgen_mask::TRepgen_mask()
|
|
: TCalendar_mask("mr1100a")
|
|
{
|
|
load_profile();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// MRP Form
|
|
///////////////////////////////////////////////////////////
|
|
|
|
class TMRP_form : public TForm
|
|
{
|
|
TCodgiac_livelli *_codgiac;
|
|
int _level;
|
|
|
|
protected:
|
|
virtual bool validate(TForm_item &, TToken_string &); // gestione dei messaggi estesi nei campi
|
|
|
|
public:
|
|
TMRP_form(const char* name, TCodgiac_livelli* c, int l);
|
|
virtual ~TMRP_form() {} ;
|
|
};
|
|
|
|
TMRP_form::TMRP_form(const char* name, TCodgiac_livelli* c, int l) : TForm(name)
|
|
{
|
|
_codgiac = c;
|
|
_level = l;
|
|
}
|
|
|
|
bool TMRP_form::validate(TForm_item &cf, TToken_string &s)
|
|
{
|
|
const TString code(s.get(0));
|
|
|
|
if (code == "_MRP")
|
|
{
|
|
TLocalisamfile& rep = cf.form().cursor()->file();
|
|
TString action(s.get(1));
|
|
TString livello, descr;
|
|
if (action == "DESCRLIV")
|
|
{
|
|
if (_level > 0)
|
|
livello = rep.get("LIVELLO");
|
|
if (livello.not_empty())
|
|
{
|
|
for (int lev=1; lev <= _level; lev++)
|
|
{
|
|
if (!_codgiac->enabled(lev))
|
|
continue;
|
|
const int starts = _codgiac->code_start(lev) -1;
|
|
const int length = _codgiac->code_length(lev);
|
|
descr << "/";
|
|
descr << livello.mid(starts,length);
|
|
}
|
|
descr << " " << _codgiac->name(_level);
|
|
descr << " " << _codgiac->group_descr(livello,_level);
|
|
}
|
|
cf.set(descr);
|
|
}
|
|
else
|
|
if (action == "LOAD")
|
|
{
|
|
//CALCOLO percentuale carico
|
|
// MESSAGE _MRP,LOAD,<id campo1>,<id campo2>
|
|
// formula: #id campo1 * 100 / #id campo2
|
|
// prende i contenuti dei campi specificati e ne calcola il rapporto % come indicato
|
|
real v1(cf.find_field(s.get(2)).get());
|
|
real v2(cf.find_field(s.get(3)).get());
|
|
|
|
v1 *= 100;
|
|
if (v2 != ZERO)
|
|
v1 /= v2;
|
|
if (v1 > ZERO && v1 <= 100.0)
|
|
cf.set(v1.string("##@,@@ %"));
|
|
else
|
|
if (v1 <= ZERO)
|
|
cf.set("");
|
|
else
|
|
cf.set("*****");
|
|
}
|
|
// Ignore any other command
|
|
}
|
|
return TForm::validate(cf, s);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// MRP report file generator
|
|
///////////////////////////////////////////////////////////
|
|
|
|
class TMRP_repgen : public TSkeleton_application
|
|
{
|
|
TRepgen_mask * _m;
|
|
TMRP_form * _form;
|
|
TCodgiac_livelli *_codgiac;
|
|
int _livello;
|
|
tipo_ordinamento _tipo_ord;
|
|
tipo_valore _tipo_val;
|
|
tipo_dettaglio _dettaglio;
|
|
|
|
protected:
|
|
void set_buckets_description();
|
|
void set_buckets();
|
|
void set_form();
|
|
virtual bool create();
|
|
virtual bool destroy();
|
|
virtual void main_loop();
|
|
|
|
public:
|
|
TMRP_repgen() { _m = NULL; }
|
|
virtual ~TMRP_repgen() {}
|
|
};
|
|
|
|
bool TMRP_repgen::create()
|
|
{
|
|
open_files(LF_MRPREP, LF_RIGHEDOC, LF_ANAMAG, LF_TAB, 0);
|
|
|
|
TConfig ini(CONFIG_DITTA, "mr");
|
|
|
|
_m = new TRepgen_mask;
|
|
_codgiac = new TCodgiac_livelli;
|
|
if (!_codgiac->enabled())
|
|
_m->hide(F_LIVDET);
|
|
return TSkeleton_application::create();
|
|
}
|
|
|
|
bool TMRP_repgen::destroy()
|
|
{
|
|
delete _codgiac;
|
|
if (_m)
|
|
delete _m;
|
|
return TSkeleton_application::destroy();
|
|
}
|
|
|
|
void TMRP_repgen::set_buckets_description()
|
|
{
|
|
TString descr;
|
|
TRepgen_mask & mask = *_m;
|
|
TDate fd(mask.start_date());
|
|
TConfig ini(CONFIG_DITTA, "mr");
|
|
const bool week_complete = ini.get_bool("WEEKCOMPLETE");
|
|
const int bucket_size = _m->get_bucket_size();
|
|
int weekd, yeard;
|
|
const short first_id = 3;
|
|
const short last_id = 15;
|
|
int last_bucket = mask.last_bucket();
|
|
|
|
if (last_bucket >= LAST_BUCKET)
|
|
last_bucket = LAST_BUCKET - 1;
|
|
--fd;
|
|
descr.cut(0);
|
|
if (bucket_size != 1)
|
|
descr << "\n";
|
|
descr << TR("Al") << " " << fd.string();
|
|
if (bucket_size == 7)
|
|
{
|
|
fd.get_week_year(weekd, yeard, week_complete);
|
|
descr << "\n" << TR("Sett.") << " " << weekd << ' ' << yeard;
|
|
}
|
|
TForm_item & ff = _form->find_field('B', odd_page, first_id);
|
|
ff.set_col_head(descr);
|
|
for (short i = 1; i <= (short) last_bucket; i++)
|
|
{
|
|
++fd;
|
|
descr.cut(0);
|
|
if (bucket_size != 1)
|
|
descr << FR("Dal"); fd.string();
|
|
descr << " " << fd.string() << " ";
|
|
if (bucket_size != 1)
|
|
{
|
|
fd += bucket_size-1;
|
|
descr << "\n" << FR("Al") << " " << fd.string();
|
|
if (bucket_size == 7)
|
|
{
|
|
fd.get_week_year(weekd, yeard, week_complete);
|
|
descr << "\n" << TR("Sett.") << " " << weekd << ' ' << yeard;
|
|
}
|
|
}
|
|
TForm_item & fi = _form->find_field('B', odd_page, first_id + i);
|
|
fi.set_col_head(descr);
|
|
}
|
|
++fd;
|
|
descr.cut(0);
|
|
descr << FR("Dal ") << fd.string();
|
|
if (bucket_size == 7)
|
|
{
|
|
fd.get_week_year(weekd, yeard, week_complete);
|
|
descr << TR("\n\nSett. ") << weekd << ' ' << yeard;
|
|
}
|
|
TForm_item & lf = _form->find_field('B', odd_page, last_id);
|
|
lf.set_col_head(descr);
|
|
}
|
|
|
|
void TMRP_repgen::set_buckets()
|
|
{
|
|
TString tmp;
|
|
|
|
// Calcola l'ultimo bucket visibile:
|
|
// Il bucket iniziale e quello finale dovrebbero essere sempre visibili
|
|
const short last_bucket = _m->last_bucket();
|
|
|
|
// Disabilita le colonne in base al numero di buckets da visualizzare
|
|
if (last_bucket < LAST_BUCKET - 1)
|
|
{
|
|
const short first_id = 4; // Bucket 1
|
|
const short last_id = 14; // Bucket 11
|
|
|
|
for (short id = first_id+last_bucket; id <= last_id; id++)
|
|
_form->find_field('B',odd_page,id).disable();
|
|
|
|
// Nella stampa scheduling linee, non esiste un totale per linea/impianto ma per articolo/liv giacenza
|
|
for (id = first_id+last_bucket+100; id <= (last_id+100); id++)
|
|
_form->find_field('B',odd_page,id).disable();
|
|
}
|
|
|
|
set_buckets_description();
|
|
|
|
// Show delle subsections necessarie
|
|
TToken_string sections;
|
|
const bool tl = _tipo_ord == linea;
|
|
|
|
switch (_dettaglio)
|
|
{
|
|
case det_articolo:
|
|
sections = tl ? "IMPIANTI|LINEE" : "";
|
|
break;
|
|
case det_giacenza:
|
|
sections = tl ? "IMPIANTI|LINEE|ARTICOLI" : "ARTICOLI";
|
|
break;
|
|
case det_impianto:
|
|
sections = tl ? "" : "ARTICOLI|LIVELLI";
|
|
break;
|
|
case det_linea:
|
|
sections = tl ? "IMPIANTI" : "ARTICOLI|LIVELLI|IMPIANTI";
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
const int items = sections.items();
|
|
for (int i=0; i<items; i++)
|
|
_form->find_field('B', odd_page, sections.get(i)).show();
|
|
|
|
if ((tl && _dettaglio == det_giacenza) || (!tl && _dettaglio >= det_giacenza))
|
|
if (_livello > 0)
|
|
{
|
|
tmp.format("LIVELLO[1,%d]", _codgiac->packed_length(_livello));
|
|
_form->find_field('B', odd_page, "LIVELLI").setcondition(tmp,_strexpr);
|
|
}
|
|
|
|
if (tl && _m->get_bool(F_CAPACITA)) // Abilitato il calcolo capacita/carico linea?
|
|
{
|
|
_form->find_field('B', odd_page, 238).show();
|
|
short lid = 240 + last_bucket;
|
|
if (lid > 250)
|
|
lid = 250;
|
|
for (short id = 240; id<lid; id++)
|
|
_form->find_field('B', odd_page, id).show();
|
|
}
|
|
|
|
if (tl)
|
|
{
|
|
if (_tipo_val == quantita) // Stampa scheduling, disabilita totali per linea/impianto)
|
|
{
|
|
// Abilita i campi per l'unita di misura sui totali per livello giac. e articolo
|
|
_form->find_field('B', odd_page, 2).show();
|
|
_form->find_field('B', odd_page, 302).show();
|
|
_form->find_field('B', odd_page, 402).show();
|
|
_form->find_field('B', odd_page, 502).show();
|
|
for (short id = 201; id <= 215; id++)
|
|
_form->find_field('B', odd_page, id).hide(); // Riga totale linea
|
|
for (id = 101; id <= 115; id++)
|
|
_form->find_field('B', odd_page, id).hide(); // Riga totale impianto
|
|
}
|
|
}
|
|
else
|
|
if (_tipo_val == carico) // Stampa carico per articoli
|
|
{
|
|
// Nasconde la colonna delle unita' di misura.
|
|
// Il calcolo capacita' non ha senso e quindi viene omesso in
|
|
// stampa, sebbene sia possibile effettuarne il calcolo.
|
|
_form->find_field('B', odd_page, 2).hide();
|
|
_form->find_field('B', odd_page, 102).hide();
|
|
_form->find_field('B', odd_page, 202).hide();
|
|
_form->find_field('B', odd_page, 302).hide();
|
|
_form->find_field('B', odd_page, 402).hide();
|
|
}
|
|
|
|
// Prende il titolo della stampa dal nome profilo
|
|
_form->find_field('H', odd_page,4).set(_m->get(DLG_PROFILE));
|
|
}
|
|
|
|
void TMRP_repgen::set_form()
|
|
{
|
|
TString expr,tmp;
|
|
TString from(_m->get(_tipo_ord == articolo ? F_ARTFROM : F_IMPIANTOFROM));
|
|
TString to(_m->get(_tipo_ord == articolo ? F_ARTTO : F_IMPIANTOTO));
|
|
|
|
expr.format("(TIPO==\"R\")");
|
|
if (_tipo_ord == articolo)
|
|
{
|
|
if (from.not_empty())
|
|
{
|
|
expr << "&&";
|
|
tmp.format("(CODART>=\"%s\")",(const char*)from);
|
|
expr << tmp;
|
|
}
|
|
if (to.not_empty())
|
|
{
|
|
if (expr.not_empty())
|
|
expr << "&&";
|
|
tmp.format("(CODART<=\"%s\")",(const char*)to);
|
|
expr << tmp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (from.not_empty())
|
|
{
|
|
expr << "&&";
|
|
tmp.format("(IMPIANTO>=\"%s\")",(const char*)from);
|
|
expr << tmp;
|
|
}
|
|
|
|
if (to.not_empty())
|
|
{
|
|
if (expr.not_empty())
|
|
expr << "&&";
|
|
tmp.format("(IMPIANTO<=\"%s\")",(const char*)to);
|
|
expr << tmp;
|
|
}
|
|
|
|
|
|
from = _m->get(F_LINEAFROM);
|
|
to = _m->get(F_LINEAFROM);
|
|
|
|
if (from.not_empty())
|
|
{
|
|
if (expr.not_empty())
|
|
expr << "&&";
|
|
tmp.format("(LINEA>=\"%s\")",(const char*)from);
|
|
expr << tmp;
|
|
}
|
|
|
|
if (to.not_empty())
|
|
{
|
|
if (expr.not_empty())
|
|
expr << "&&";
|
|
tmp.format("(LINEA<=\"%s\")",(const char*)to);
|
|
expr << tmp;
|
|
}
|
|
}
|
|
|
|
if (expr.not_empty()) // Filtronzo... il filtro gonzo
|
|
_form->cursor()->setfilter(expr);
|
|
|
|
set_buckets();
|
|
}
|
|
|
|
void TMRP_repgen::main_loop()
|
|
{
|
|
KEY k;
|
|
while ((k = _m->run()) != K_QUIT)
|
|
{
|
|
const bool use_file = _m->get_bool(F_USAFILE);
|
|
if (use_file || _m->elabora())
|
|
{
|
|
if (k == K_ENTER)
|
|
{
|
|
TFilename report_name(_m->get(F_REPORT));
|
|
|
|
if (report_name.empty())
|
|
{
|
|
_tipo_ord = (tipo_ordinamento) _m->get_int(F_ORDINAMENTO);
|
|
_tipo_val = (tipo_valore) _m->get_int(F_VAL2PRINT);
|
|
_livello = _m->get_int(F_LIVDET);
|
|
|
|
const bool delfile = !_m->get_bool(F_GENREPORT) && !use_file;
|
|
|
|
if (delfile)
|
|
{
|
|
_m->reset(F_FILENAME);
|
|
}
|
|
|
|
// Utilizza questo file per la stampa del form
|
|
_dettaglio = (tipo_dettaglio) _m->get_int(_tipo_ord == articolo ? F_DETTAGLIO1 : F_DETTAGLIO2);
|
|
_form = new TMRP_form(_tipo_ord == articolo ? "mr1100a" : "mr1100b", _codgiac, _livello);
|
|
TRelation* rel = _form->relation();
|
|
|
|
if (!delfile)
|
|
{
|
|
TFilename fname(_m->get(F_FILENAME)); // Presente 24 ore su 24
|
|
fname.insert("%"); // e questo dove lo mettiamo?
|
|
TIsamtempfile * report = new TIsamtempfile(LF_MRPREP,fname, FALSE, delfile);
|
|
rel->replace(report);
|
|
}
|
|
set_form();
|
|
|
|
const int hh = 7;
|
|
const int fl = printer().formlen();
|
|
|
|
int rows[4]; // Righe orizzontali
|
|
rows[0] = hh-4;
|
|
rows[1] = hh;
|
|
rows[2] = fl;
|
|
rows[3] = 0;
|
|
_form->genera_intestazioni(odd_page, hh-3);
|
|
_form->genera_fincatura(odd_page, hh-4, fl, rows);
|
|
|
|
// stampa
|
|
if (_form->cursor()->items() > 0)
|
|
_form->print();
|
|
// report non va cancellato, poiche' ne viene fatta la sostituzione nella relazione del form
|
|
// quindi la delete viene gia' fatta alla distruzione di _form
|
|
delete _form;
|
|
_form = NULL;
|
|
}
|
|
else
|
|
{
|
|
TReport_book book;
|
|
TReport rep;
|
|
|
|
if (rep.load(report_name))
|
|
{
|
|
book.add(rep);
|
|
if (book.pages() > 0)
|
|
book.print_or_preview();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int mr1100(int argc, char* argv[])
|
|
{
|
|
TMRP_repgen a;
|
|
a.run(argc, argv, TR("Generazione MRP reports"));
|
|
return 0;
|
|
}
|