campo-sirio/mr/mr2100.cpp
alex a0a36d6b33 Patch level : XX.282
Files correlati     :
Ricompilazione Demo : [ ]
Commento            : Riportata la versione 01.05 patch 282


git-svn-id: svn://10.65.10.50/trunk@8019 c028cbd2-c16b-5b4b-a496-9718f37d4682
1999-04-06 15:34:39 +00:00

1674 lines
46 KiB
C++
Executable File

#include <applicat.h>
#include <defmask.h>
#include <progind.h>
#include <sheet.h>
#include <tabutil.h>
#include <utility.h>
#include "mrplib.h"
#include "..\mg\mglib.h"
#include "..\ve\velib.h"
#include "..\ve\veconf.h"
#include "mr2100.h"
#include "mr2100a.h"
///////////////////////////////////////////////////////////
// TMRP_record
///////////////////////////////////////////////////////////
const real& TMRP_record::add_gross_req(const real & val)
{
_gross_requirement += val;
return _gross_requirement;
}
const real& TMRP_record::add_sched_rec(const real & val)
{
_sched_receipts += val;
return _sched_receipts;
}
const real& TMRP_record::add_net_req(const real & val)
{
_net_requirement += val;
return _net_requirement;
}
TObject * TMRP_record::dup() const
{
TMRP_record *o= new TMRP_record(_time);
o->TMRP_record::operator=(*this);
return o;
}
TMRP_record & TMRP_record::operator=(const TMRP_record & a)
{
_gross_requirement=a._gross_requirement;
_on_hand=a._on_hand;
_sched_receipts=a._sched_receipts;
_released_receipts=a._released_receipts;
_net_requirement=a._net_requirement;
_planned_orders=a._planned_orders;
return *this;
}
TMRP_record::TMRP_record(const TMRP_time& t)
: _time(t)
{
}
///////////////////////////////////////////////////////////
// TMRP_line
///////////////////////////////////////////////////////////
TArticolo_giacenza *TMRP_line::_articolo_giac=NULL;
void TMRP_line::lotti_riordino(real & minimo, real & increm) const
{
_articolo_giac->read(_codart);
const int i=_articolo_giac->find_mag("", _codmag);
if (i>=0)
{
const TRectype & rec=_articolo_giac->mag("").row(i);
minimo = rec.get_real(MAG_LOTTORIOR);
increm = rec.get_real(MAG_LOTTOIRIOR);
} else
minimo=increm=0;
if (minimo.is_zero())
minimo = _articolo_giac->get_real(MAG_LOTTORIOR);
if (increm.is_zero())
increm = _articolo_giac->get_real(MAG_LOTTOIRIOR);
}
int TMRP_line::add_son(const real& qta, TMRP_line* son)
{
CHECK(son, "Can't add NULL TMRP_line son");
CHECK(son != this, "Hermafrodite TMRP_line");
_qta_sons.add(qta);
return _sons.add(son);
}
bool TMRP_line::is_son(const TCodice_articolo& art) const
{
for (int i = sons()-1; i >= 0; i--)
{
const TMRP_line& r = son(i);
if (r.codice() == art)
break;
}
return i >= 0;
}
int TMRP_line::compare(const TSortable& s) const
{
const TMRP_line& line = (const TMRP_line&)s;
const bool imfather = sons() > 0;
const bool hesfather = line.sons() > 0;
if (imfather && (!hesfather || is_son(line.codice())))
return +1;
if (hesfather && (!imfather || line.is_son(codice())))
return -1;
return 0;
}
TObject* TMRP_line::dup() const
{
TMRP_line * o=new TMRP_line(*this);
return o;
}
TMRP_line & TMRP_line::operator=(const TMRP_line & a)
{
//key
_codart=a._codart;
_livgiac=a._livgiac;
_codmag=a._codmag;
_codimp=a._codimp;
_codlin=a._codlin;
_codcli=a._codcli;
// contents
_description=a._description;
_sons=a._sons;
_qta_sons=a._qta_sons;
_req_per_bucket=a._req_per_bucket;
_articolo_giac= new TArticolo_giacenza(a._articolo_giac->codice());
return *this;
}
TMRP_record& TMRP_line::record(int i) const
{
TMRP_record* rec = (TMRP_record*)_req_per_bucket.objptr(i);
CHECKD(rec != NULL, "Invalid MRP record ", i);
return *rec;
}
TMRP_record& TMRP_line::record(const TMRP_time& t) const
{
const int last = last_bucket();
for (int i = 0; i <= last; i++)
{
TMRP_record& rec = record(i);
const int cmp = rec.time().compare(t);
if (cmp == 0)
return rec;
if (cmp > 0)
{
((TArray&)_req_per_bucket).insert(NULL, i);
break;
}
}
TMRP_record* rec = new TMRP_record(t);
((TArray&)_req_per_bucket).add(rec, i);
return *rec;
}
// approssima in base al lotto minimo; prevede valori negativi
real TMRP_line::sizeup_net_requirement(int i, const real &val)
{
real req; // Azzera net requirement
if (val < ZERO)
{
req = -val; // Rende positiva la richiesta effettiva
real lm,li;
lotti_riordino(lm,li);
int cazzo;
if (!lm.is_zero() && !li.is_zero() )
cazzo=1;
if (req > lm) // Se la richiesta supera il lotto minimo
{
if (li > ZERO)
{
// Calcola il numero di lotti incrementali
real lotti = (req - lm) / li;
lotti.ceil();
// La quantita' richiesta = minimo + lotti * dim.lotti
req = lm + lotti * li;
}
}
else
req = lm; // Forza la richiesta almeno al lotto minimo
}
set_net_req(i, req);
return req;
}
const real& TMRP_line::add_gross_req(const TMRP_time& t, const real& val)
{ return record(t).add_gross_req(val); }
const real& TMRP_line::add_sched_rec(const TMRP_time& t, const real &val)
{ return record(t).add_sched_rec(val);}
const real & TMRP_line::set_net_req(int i, const real &val)
{
if (val>ZERO)
return record(i).set_net_req(val);
else
return record(i).set_net_req(ZERO);
}
const real & TMRP_line::add_net_req(const TMRP_time &t, const real &val)
{ return record(t).add_net_req(val); }
const real & TMRP_line::set_on_hand(int i, const real &val)
{ return record(i).set_on_hand(val);}
const real & TMRP_line::set_on_hand(const TMRP_time &t, const real &val)
{ return record(t).set_on_hand(val);}
const TMRP_time& TMRP_line::lead_time(int i, TMRP_time& t) const
{
t = record(i).time();
TLocalisamfile dist(LF_DIST);
dist.put("CODDIST", codice());
if (dist.read() == NOERR)
{
int days = dist.get_int("LEADTIME");
long hours = dist.get_long("LEADHOURS");
t.sub_time(days, hours);
}
else
{
TLocalisamfile anamag(LF_ANAMAG);
anamag.put(ANAMAG_CODART, codice());
if (anamag.read() == NOERR)
{
int days = anamag.get_int("LEADTIME");
t.sub_time(days);
}
}
return t;
}
real &TMRP_line::giacenza_attuale(real &g) const
{
TDate d(TODAY);
return giacenza_attuale(g, d);
}
real &TMRP_line::giacenza_attuale(real &g, const TDate &d) const
{
_articolo_giac->read(codice());
g = _articolo_giac->giacenza_anno(codmag(),livgiac(),d.year());
return g;
}
real &TMRP_line::scorta_minima(real &g) const
{
TDate d(TODAY);
return scorta_minima(g,d);
}
real &TMRP_line::scorta_minima(real &g, const TDate &d) const
{
_articolo_giac->read(codice());
g = _articolo_giac->scorta_minima(codmag(),livgiac(),d.year());
return g;
}
TMRP_line::TMRP_line(const char* art, const char* giac,
const char* mag, const char* imp,
const char* lin, long codcli)
: _codart(art), _livgiac(giac),
_codmag(mag), _codimp(imp), _codlin(lin), _codcli(codcli)
{
if (_articolo_giac==NULL)
_articolo_giac= new TArticolo_giacenza();
}
TMRP_line::TMRP_line(const TMRP_line&a)
{
TMRP_line::operator=(a);
}
///////////////////////////////////////////////////////////
// TMRP_lines
///////////////////////////////////////////////////////////
TSortable* TMRP_lines::new_obj(const TToken_string& key) const
{
TCodice_articolo art; key.get(0, art); art.trim();
TString80 gia; key.get(1, gia); gia.trim();
TString16 mag; key.get(2, mag); mag.trim();
TString16 imp; key.get(3, imp); imp.trim();
TString16 lin; key.get(4, lin); lin.trim();
TString16 clifor ; key.get(5, clifor);
return new TMRP_line(art, gia, mag, imp, lin, atol(clifor));
}
TMRP_lines::TMRP_lines(const TMRP_lines & a)
{
TMRP_lines::operator=(a);
}
TObject* TMRP_lines::dup() const
{
return new TMRP_lines(*this);;
}
TMRP_lines & TMRP_lines::operator= (const TMRP_lines &a)
{
TMRP_array::operator= (a);
_ignore_mag=a._ignore_mag;
_ignore_imp=a._ignore_imp;
_ignore_lin=a._ignore_lin;
return *this;
}
TMRP_line* TMRP_lines::find(const TCodice_articolo& codart,
const TString& gia, const TString& mag,
const TString& imp, const TString& lin,
long codcli,
bool create)
{
_key = codart;
_key.add(gia);
if (_ignore_mag)
_key.add(" ",2);
else
_key.add(mag,2);
if (_ignore_imp)
_key.add(" ",3);
else
_key.add(imp,3);
if (_ignore_lin)
_key.add(" ",4);
else
_key.add(lin,4);
_key.add(codcli,5);
TSortable* s = create ? add_obj(_key) : find_obj(_key);
return (TMRP_line*)s;
}
TMRP_lines::TMRP_lines()
: _ignore_mag(FALSE), _ignore_imp(FALSE), _ignore_lin(FALSE)
{ }
TMRP_lines::~TMRP_lines()
{ destroy(); }
///////////////////////////////////////////////////////////
// TRiga_ordine
///////////////////////////////////////////////////////////
#define SORT_COMPLETE 13 //
#define SORT_BY_DFA -1 // data documento + data di consegna + fornitore + articolo
#define SORT_BY_DAF -2 // data documento + data di consegna + articolo + fornitore
#define SORT_BY_FAD -3 //
#define SORT_BY_FDA -4 //
#define SORT_BY_AFD -5 //
#define SORT_BY_ADF -6 //
class TRiga_ordine : public TToken_string
{
public:
int compare(const TToken_string& r, int level = SORT_COMPLETE) const;
int compare_field(TString &str0, TString &str1,short field_no) const;
TRiga_ordine& operator=(TToken_string& r);
TRiga_ordine& operator+=(TRiga_ordine& r);
TRiga_ordine() : TToken_string(128) { }
TRiga_ordine(const TDate& datadoc, long forn,
const TMRP_line& line, int bucket,
TCodgiac_livelli& livelli, const real & price);
virtual ~TRiga_ordine() { }
};
int TRiga_ordine::compare(const TToken_string& riga, int level) const
{
TString16 str0, str1;
int cmp = 0;
if (level>=0)
{
for (int i = 2; i <= level && cmp == 0; i++)
{
get(i, str0);
riga.get(i, str1);
cmp=compare_field(str0,str1,short(i+FIRST_FIELD));
}
} else {
// ordinamenti non standard
short f;
short fields_DFA[] = {F_DATADOC, F_DATACONS, F_ORD_TYPE, F_FORNITORE, F_ARTICOLO};
short fields_DAF[] = {F_DATADOC, F_DATACONS, F_ARTICOLO, F_ORD_TYPE, F_FORNITORE};
short fields_AFD[] = {F_ARTICOLO, F_ORD_TYPE, F_FORNITORE, F_DATADOC, F_DATACONS};
short fields_ADF[] = {F_ARTICOLO, F_ORD_TYPE, F_DATADOC, F_DATACONS, F_FORNITORE};
short fields_FAD[] = {F_ORD_TYPE, F_FORNITORE, F_ARTICOLO, F_DATADOC, F_DATACONS};
short fields_FDA[] = {F_ORD_TYPE, F_FORNITORE, F_DATADOC, F_DATACONS, F_ARTICOLO};
for (int i = 0; i < 5 && cmp == 0; i++)
{
switch (level)
{
case SORT_BY_DFA:
f=fields_DFA[i]; break;
case SORT_BY_DAF:
f=fields_DAF[i]; break;
case SORT_BY_FAD:
f=fields_FAD[i]; break;
case SORT_BY_FDA:
f=fields_FDA[i]; break;
case SORT_BY_ADF:
f=fields_ADF[i]; break;
case SORT_BY_AFD:
f=fields_AFD[i]; break;
default:
NFCHECK("Ordinamento sconosciuto");
break;
}
get(f-FIRST_FIELD, str0);
riga.get(f-FIRST_FIELD, str1);
cmp=compare_field(str0,str1,f);
}
}
return cmp;
}
int TRiga_ordine::compare_field(TString &str0, TString &str1,short field_no) const
{
int cmp;
switch (field_no)
{
case F_DATADOC:
case F_DATACONS:
{
if (str0 != str1)
{
const TDate mine(str0);
const TDate his(str1);
cmp = mine > his ? +1 : -1;
}
else
cmp = 0;
}
break;
case F_FORNITORE: // data doc
{
const long f0 = atol(str0);
const long f1 = atol(str1);
cmp = f0 == f1 ? 0 : (f0 > f1 ? +1 : -1);
}
break;
default:
cmp = str0.compare(str1);
break;
}
return cmp;
}
TRiga_ordine& TRiga_ordine::operator=(TToken_string& r)
{
this->TToken_string::operator=(r);
return *this;
}
TRiga_ordine& TRiga_ordine::operator+=(TRiga_ordine& r)
{
CHECK(compare(r) == 0, "Can't add incompatible order line");
real qta = get(13);
qta += real(r.get(13));
add(qta.string(), 13);
return *this;
}
TRiga_ordine::TRiga_ordine(const TDate& datadoc, long forn, const TMRP_line& line, int bucket, TCodgiac_livelli& lg, const real & price)
: TToken_string(128)
{
add(" ",F_SELECTED-FIRST_FIELD);
add("F",F_ORD_TYPE-FIRST_FIELD);
add(datadoc.string(),F_DATADOC-FIRST_FIELD);
const TMRP_time& time = line.time(bucket);
add(time.date().string(),F_DATACONS-FIRST_FIELD);
add(forn,F_FORNITORE-FIRST_FIELD);
add(line.codice(),F_ARTICOLO-FIRST_FIELD);
const TString& liv = line.livgiac();
for (int l = 1; l <= 4; l++)
{
if (lg.enabled(l))
add(liv.mid(lg.code_start(l), lg.code_length(l)),F_LIV1+l-1-FIRST_FIELD);
else
add(" ",F_LIV1+l-1-FIRST_FIELD);
}
add(line.codmag().left(3),F_MAGAZZINO-FIRST_FIELD);
add(line.codmag().mid(3),F_DEPOSITO-FIRST_FIELD);
add(line.codimp(),F_CODIMP-FIRST_FIELD);
add(line.codlin(),F_CODLIN-FIRST_FIELD);
add(line.net_requirement(bucket).string(),F_QUANTITA-FIRST_FIELD);
const TCodice_um um;
const TQuantita qta(line.codice(), um, ZERO);
add(qta.um(),F_UM-FIRST_FIELD);
add(price.string(),F_PREZZO-FIRST_FIELD);
add(line.description(),F_DESCART-FIRST_FIELD);
}
///////////////////////////////////////////////////////////
// TLav_finder Trova la prima lavorazione di un articolo
///////////////////////////////////////////////////////////
class TLav_finder : public TAssoc_array
{
TDecoder _lnp;
bool _keep_imp;
public:
void init(bool ki = TRUE);
const TString& lin2imp(const TString& lin);
TLavorazione& art2lav(const TCodice_articolo& art);
void art2magimpline(const TCodice_articolo& art,
TString& mag, TString& imp,
TString& lin);
TLav_finder();
virtual ~TLav_finder() { }
};
void TLav_finder::init(bool ki)
{
_keep_imp = ki;
TAssoc_array::destroy();
_lnp.destroy();
}
TLavorazione& TLav_finder::art2lav(const TCodice_articolo& art)
{
TLavorazione* lav = (TLavorazione*)objptr(art);
if (lav == NULL)
{
TDistinta_tree tree;
TArray boom;
tree.set_root(art);
tree.explode(boom, FALSE, RAGGR_EXP_UMBASE, 1, "L");
TString16 codlav;
if (boom.items() > 0)
codlav = ((TRiga_esplosione&)boom[0]).articolo();
lav = new TLavorazione(codlav);
add(art, lav);
}
return *lav;
}
void TLav_finder::art2magimpline(const TCodice_articolo& art,
TString& mag, TString& imp,
TString& lin)
{
TLavorazione& lav = art2lav(art);
if (imp.blank() && !lin.blank())
imp = _lnp.decode(lin);
lin.cut(0);
const int ll = _keep_imp ? lav.linee() : lav.linee_standard();
for (int l = 0; l < ll; l++)
{
const TString& linea = lav.cod_linea(l);
const bool goodimp = imp.empty() || _lnp.decode(linea) == imp;
if (l == 0 || goodimp)
{ lin = linea; lin.trim(); }
if (goodimp)
break;
}
if (lin.not_empty())
{
const TLinea_prod lnp(lin) ;
imp = lnp.codimp();
mag = lnp.codmag();
}
else
{
if (!_keep_imp)
imp = mag = "";
}
}
TLav_finder::TLav_finder() : _lnp("LNP", "S6"), _keep_imp(TRUE)
{ }
///////////////////////////////////////////////////////////
// TMatResPlanning
///////////////////////////////////////////////////////////
class TMatResMask;
class TMatResPlanning : public TSkeleton_application
{
TMRP_lines _articles;
TLav_finder _artinfo;
private:
bool gross2net_logic(TMRP_line &curr_article, int bucket,bool lotsizing_f, bool lotsizing_p);
bool build_gross_requirements(const TMatResMask& m);
bool build_sched_receipts(const TMatResMask& m);
bool explode_articles(const TMatResMask& m);
bool test_codnum(const TCodice_numerazione& num, TString_array& a) const;
bool test_status(const TRectype& doc, TString_array& a) const;
protected:
bool preprocess_cycle(const TMatResMask& m); // req iniziale dai docs
bool net_requirement_cycle(const TMatResMask& m);
bool build_orders(TMatResMask& m);
public:
virtual void main_loop();
virtual bool firm_change_enabled() const { return FALSE; }
void compute(TMatResMask& m);
bool emit_orders(TMatResMask& m);
};
TMatResPlanning& app() { return (TMatResPlanning&)main_app(); }
///////////////////////////////////////////////////////////
// TMatResMask
///////////////////////////////////////////////////////////
class TMatResMask : public TCalendar_mask
{
TCodgiac_livelli _livelli;
TCondizione_vendita *_condv;
void clear_sheets();
protected:
void round_field(TMask_field& fld, bool up) const;
virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);
int find_pos(const TRiga_ordine& r, int& cmp) const;
void sort_orders();
bool orders_selected(char type);
void select_orders(char type);
public:
TCodgiac_livelli & livelli_giacenza() {return _livelli;}
int round_date(TDate& date, bool up = FALSE) const;
int add_order_line(const TDate& data, long forn, const TMRP_line& l, int bucket);
TMatResMask();
virtual ~TMatResMask() { }
};
int TMatResMask::round_date(TDate& date, bool up) const
{
// Dimensione del bucke in giorni
int bucket_size = get_int(F_BUCKET) * 7;
if (bucket_size < 7) bucket_size = 7;
// Riporta la data al primo lunedi prima dell'inizio
TDate inizio = get(F_DADATA);
const int wday = inizio.wday();
if (wday > 1) inizio -= wday-1;
// Calcola il bucket di appartenenza
const int days = int(date - inizio);
const int bucket = days / bucket_size;
if (up) // Arrotonda alla fine del bucket
date = inizio + long((bucket+1) * bucket_size - 1);
else // Arrotonda all'inizio del bucket
date = inizio + long(bucket * bucket_size);
return bucket;
}
void TMatResMask::round_field(TMask_field& fld, bool up) const
{
TDate date = fld.get();
if (date.ok())
{
round_date(date, up);
fld.set(date);
}
}
bool TMatResMask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
const char * confirm_msg="Le attuali %d righe di ordine verranno perse. Confermi ?";
switch (o.dlg())
{
case F_DADATA:
if (e == fe_modify)
round_field(o, FALSE);
break;
case F_ADATA:
if (e == fe_modify)
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_NUM_ORC:
case F_TIPI_ORC:
case F_NUM_ORF:
case F_TIPI_ORF:
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("E' necessario inserire almeno una riga");
}
break;
case F_ORDINI:
{
TSheet_field& s = (TSheet_field&)o;
switch(e)
{
case se_query_add:
return FALSE;
default:
break;
}
}
break;
case F_YEAR:
case F_IMPIANTO:
case F_LINEA:
if (e == fe_modify || (o.dlg() == F_YEAR && e == fe_init ))
update_calendar(F_CALENDAR, F_YEAR, F_IMPIANTO, F_LINEA);
break;
case DLG_CANCEL:
if (e == fe_button)
if (jolly == 0L)
{
TSheet_field& s = sfield(F_ORDINI);
const int it=s.items();
if (it==0 || yesno_box(confirm_msg, it))
{
enable(-1);
s.destroy();
s.force_update();
} else
return FALSE;
}
break;
case DLG_ELABORA:
if (e == fe_button && check_fields())
{
TSheet_field& s = sfield(F_ORDINI);
const int it=s.items();
if (it==0 || yesno_box(confirm_msg, it))
{
s.destroy();
app().compute(*this);
enable(DLG_SAVEREC, s.items() > 0);
enable(-1, s.items() == 0);
sort_orders();
s.set_focus();
}
}
break;
case F_SORT_ORDINI:
if (e == fe_modify)
sort_orders();
break;
case F_SELECT_ORDF:
if (e == fe_button)
select_orders('F');
break;
case F_SELECT_ORDP:
if (e == fe_button)
select_orders('P');
break;
case DLG_SAVEREC:
if (e == fe_button && check_fields())
{
if (!app().emit_orders(*this))
message_box("Nessun ordine generato");
}
break;
case DLG_PROFILE:
if (e == fe_modify)
{
clear_sheets();
enable(-1);
}
break;
default:
break;
};
return TRUE;
}
void TMatResMask::clear_sheets()
{
TSheet_field &sa=sfield(F_ORDINI);
if (sa.items()>0)
{
sa.destroy();
sa.force_update();
}
}
// Per ora scansione lineare, in futuro ricerca binaria
int TMatResMask::find_pos(const TRiga_ordine& r, int& cmp) const
{
TSheet_field& s = sfield(F_ORDINI);
TString_array& a = s.rows_array();
cmp = +1;
for (int i = 0; i < a.items(); i++)
{
const TRiga_ordine& riga = (const TRiga_ordine&)a[i];
cmp = r.compare(riga);
if (cmp <= 0)
break;
}
return i;
}
int TMatResMask::add_order_line(const TDate& datadoc, long forn, const TMRP_line& line, int bucket)
{
TRiga_ordine* r = new TRiga_ordine(datadoc, forn, line, bucket, _livelli, ZERO);
int cmp;
const int pos = find_pos(*r, cmp);
TSheet_field& s = sfield(F_ORDINI);
TString_array& a = s.rows_array();
if (cmp == 0)
{
TRiga_ordine& riga = (TRiga_ordine&)a[pos];
riga += *r;
delete r;
}
else
{
if (1 || cmp > 0)
a.add(r);
else
a.insert(r, pos);
}
return pos;
}
static int order_compareDFA(TSheet_field &s,int i1,int i2)
{
const TRiga_ordine& r1 = (TRiga_ordine&)s.row(i1);
const TRiga_ordine& r2 = (TRiga_ordine&)s.row(i2);
return r1.compare(r2,SORT_BY_DFA);
}
static int order_compareDAF(TSheet_field &s,int i1,int i2)
{
const TRiga_ordine& r1 = (TRiga_ordine&)s.row(i1);
const TRiga_ordine& r2 = (TRiga_ordine&)s.row(i2);
return r1.compare(r2,SORT_BY_DAF);
}
static int order_compareAFD(TSheet_field &s,int i1,int i2)
{
const TRiga_ordine& r1 = (TRiga_ordine&)s.row(i1);
const TRiga_ordine& r2 = (TRiga_ordine&)s.row(i2);
return r1.compare(r2,SORT_BY_AFD);
}
static int order_compareADF(TSheet_field &s,int i1,int i2)
{
const TRiga_ordine& r1 = (TRiga_ordine&)s.row(i1);
const TRiga_ordine& r2 = (TRiga_ordine&)s.row(i2);
return r1.compare(r2,SORT_BY_ADF);
}
static int order_compareFAD(TSheet_field &s,int i1,int i2)
{
const TRiga_ordine& r1 = (TRiga_ordine&)s.row(i1);
const TRiga_ordine& r2 = (TRiga_ordine&)s.row(i2);
return r1.compare(r2,SORT_BY_FAD);
}
static int order_compareFDA(TSheet_field &s,int i1,int i2)
{
const TRiga_ordine& r1 = (TRiga_ordine&)s.row(i1);
const TRiga_ordine& r2 = (TRiga_ordine&)s.row(i2);
int cmp=r1.compare(r2,SORT_BY_FDA);
return cmp;
}
void TMatResMask::sort_orders()
{
TSheet_field& s = sfield(F_ORDINI);
TSheet_field& a = s;
switch (-get_int(F_SORT_ORDINI))
{
case SORT_BY_DAF:
a.sort(order_compareDAF); break;
case SORT_BY_FAD:
a.sort(order_compareFAD); break;
case SORT_BY_FDA:
a.sort(order_compareFDA); break;
case SORT_BY_AFD:
a.sort(order_compareAFD); break;
case SORT_BY_ADF:
a.sort(order_compareADF); break;
case SORT_BY_DFA:
default:
a.sort(order_compareDFA); break;
}
s.force_update();
}
bool TMatResMask::orders_selected(char type)
{
TSheet_field& s = sfield(F_ORDINI);
const int it=s.items();
for (int r=0; r< it; r++)
{
if ( *s.cell(r, s.cid2index(F_ORD_TYPE))==type
&& *s.cell(r, s.cid2index(F_SELECTED))=='X')
return TRUE;
}
return FALSE;
}
void TMatResMask::select_orders(char type)
{
TSheet_field& s = sfield(F_ORDINI);
bool on=!orders_selected(type);
const int it=s.items();
for (int r=0; r< it; r++)
{
if ( *s.cell(r, s.cid2index(F_ORD_TYPE))==type )
{
s.row(r).add(on ? "X" : " ", s.cid2index(F_SELECTED));
s.force_update(r);
}
}
TString msg=format("Ordini %s %s",type=='F' ? "fornitore" : "di produzione" ,on ? "selezionati" :" de-selezionati");
xvt_statbar_set(msg);
}
TMatResMask::TMatResMask() : TCalendar_mask("mr2100a")
{
_condv = NULL;
TSheet_field& sf = sfield(F_ORDINI);
_livelli.set_sheet_columns(sf, F_LIV1);
TConfig ini(CONFIG_DITTA, "mg");
if (!ini.get_bool("GESDEPOSITI", "mg"))
{
sf.delete_column(F_DEPOSITO);
sf.sheet_mask().hide(F_DEPOSITO);
}
if (!ini.get_bool("GESTIMPIANTI", "mr"))
{
sf.delete_column(F_CODIMP); // Elimina colonna impianto
sf.sheet_mask().hide(F_CODIMP);
sf.sheet_mask().hide(F_DESCIMP);
disable(F_NOIMP); // Forza l'ignoramento degli impianti
set(F_NOIMP, "X");
}
}
///////////////////////////////////////////////////////////
// TMatResPlanning
///////////////////////////////////////////////////////////
bool TMatResPlanning::test_codnum(const TCodice_numerazione& num,
TString_array& a) const
{
bool yes = a.find(num.codice()) >= 0;
return yes;
}
bool TMatResPlanning::test_status(const TRectype& doc,
TString_array& a) const
{
const TString16 tipodoc = doc.get(DOC_TIPODOC);
const int statodoc = doc.get_int(DOC_STATO);
bool yes = FALSE;
for (int i = a.items()-1; i >= 0 && !yes; i--)
{
TToken_string& riga = a.row(i);
const char* t = riga.get(0);
if (tipodoc == t)
{
const int state_fr = riga.get_int(F_DASTATO - FIRST_FIELD);
const int state_to = riga.get_int(F_ASTATO - FIRST_FIELD);
yes = statodoc >= state_fr && statodoc <= state_to;
}
}
return yes;
}
static long table_items(const char* tab)
{
TRelation tabrel(tab);
TCursor tabcur (&tabrel);
long tot = tabcur.items();
return tot;
}
/////////// finished: 100%
/////////// tested : 100%
bool TMatResPlanning::build_gross_requirements(const TMatResMask& m)
{
TDate date_fr = m.get(F_DADATA);
const int bucket_fr = m.round_date(date_fr, FALSE);
TDate date_to = m.get(F_ADATA);
const int bucket_to = m.round_date(date_to, TRUE);
const int year_fr = date_fr.year();
const int year_to = date_to.year();
const bool master = m.get(F_ORC_MASTER)[0] == 'M';
// azzera l'array _requirements
const bool nomag = m.get_bool(F_NOMAG);
const bool noimp = m.get_bool(F_NOIMP);
const bool nolin = m.get_bool(F_NOLIN);
_articles.ignore(nomag, noimp, nolin);
_articles.destroy();
TTable num("%NUM");
TCodice_numerazione cod;
TRelation rel(LF_DOC);
TCursor cur(&rel);
const TRectype& curr_doc = cur.curr();
TRectype filter_fr(curr_doc), filter_to(curr_doc);
TString_array& n = m.sfield(F_NUM_ORC).rows_array();
TString_array& a = m.sfield(F_TIPI_ORC).rows_array();
TProgind pi(table_items("%NUM"), "Fase 1: caricamento fabbisogni lordi...", TRUE, TRUE);
// Scandisce tutte le numerazioni considerando solo quelle
// contenenti i tipi documento specificati nella maschera
for (int err = cod.first(num); err == NOERR; err = cod.next(num))
{
pi.addstatus(1);
if (pi.iscancelled())
return FALSE;
if (test_codnum(cod, n))
{
// Filtra il cursore in modo da limitarlo alla numerazione
// corrente ed agli anni specificati dalle due date limite
filter_fr.put(DOC_PROVV, "D");
filter_fr.put(DOC_ANNO, year_fr);
filter_fr.put(DOC_CODNUM, cod.get("CODTAB"));
filter_to.put(DOC_PROVV, "D");
filter_to.put(DOC_ANNO, year_to);
filter_to.put(DOC_CODNUM, cod.get("CODTAB"));
cur.setregion(filter_fr, filter_to);
TString cfilter;
cfilter << DOC_CODNUM << "==\"" << cod.get("CODTAB") << '"';
cur.setfilter(cfilter);
const long items = cur.items();
cur.freeze(TRUE);
// Scandisce i documenti inevasi e considera solo
// quelli con uno stato nel range corretto
for (cur = 0; cur.pos() < items; ++cur)
{
const bool evaso = curr_doc.get_bool(DOC_DOCEVASO);
if (evaso)
continue;
TDate doc_cons = curr_doc.get(DOC_DATACONS);
if (!doc_cons.ok())
doc_cons = curr_doc.get(DOC_DATADOC);
if (test_status(curr_doc, a))
{
// Scandisce le righe articolo e memorizza
// le quantita' richieste
TDocumento doc(cur.curr());
for (int r = doc.physical_rows(); r > 0; r--)
{
// Seleziona le righe articolo non ancora evase
const TRiga_documento& riga = doc[r];
TDate datacons = riga.get(RDOC_DATACONS);
if (!datacons.ok()) datacons = doc_cons;
// Data consegna troppo avanti
if (datacons > date_to)
continue;
if (riga.is_articolo())
{
const real qta = riga.qtaresidua();
if (qta > ZERO)
{
const TCodice_articolo art = riga.get(RDOC_CODARTMAG);
const TString16 liv = riga.get(RDOC_LIVELLO);
const TString16 mag = nomag ? EMPTY_STRING : riga.get(RDOC_CODMAG);
const TString16 imp = noimp ? EMPTY_STRING : riga.get(RDOC_IMPIANTO);
const TString16 lin = nolin ? EMPTY_STRING : riga.get(RDOC_LINEA);
const TCodice_um um = riga.get(RDOC_UMQTA);
TQuantita q(art, um, qta);
q.convert2umbase();
TMRP_line* line = _articles.find(art, liv, mag, imp, lin, 0L);
if (line == NULL)
{
// nuova linea
line = _articles.find(art, liv, mag, imp, lin, 0L, TRUE);
line->set_description(riga.get(RDOC_DESCR));
}
const TMRP_time t(datacons, 0, imp, lin);
line->add_gross_req(t, q.val());
if (master)
line->add_net_req(t, q.val());
}
}
}
}
}
cur.freeze(FALSE);
}
}
return _articles.items() > 0L;
}
/////////// finished: 100%
/////////// tested : 100%
bool TMatResPlanning::explode_articles(const TMatResMask& m)
{
TDistinta_tree distinta; // albero distinta
TArray boom; // array per i figli
int level = 1;
TProgind* pi = NULL;
// Inizializza la cache delle lavorazioni
_artinfo.init(m.get_bool(F_KEEP_IMP));
// Scandisce gli articoli inseriti dal gross requirements ed
// accoda tutti gli articoli risultanti dalla loro esplosione
for (long a = 0; a < _articles.items(); a++)
{
if (pi && pi->isfinished())
{
delete pi;
pi = NULL;
}
if (pi == NULL)
{
TString80 msg;
msg.format("Fase 2: esplosione articoli (livello %d)", level++);
pi = new TProgind(_articles.items()-a, msg, TRUE, TRUE);
}
pi->addstatus(1);
if (pi->iscancelled())
return FALSE;
TMRP_line& line = _articles[a];
if (distinta.set_root(line.codice()))
{
distinta.set_global("_DISTINTA", line.codice());
distinta.set_global("_LIVELLO", line.livgiac());
distinta.set_global("_MAGAZZINO", line.codmag());
distinta.set_global("_IMPIANTO", line.codimp());
distinta.set_global("_LINEA", line.codlin());
distinta.explode(boom, FALSE, RAGGR_EXP_UMBASE, 1, "AV");
for (int i = 0; i < boom.items(); i++)
{
const TRiga_esplosione& riga = (const TRiga_esplosione&)boom[i];
const TCodice_articolo& art = riga.articolo();
const TRectype& articolo=cache().get(LF_ANAMAG,art);
bool add=TRUE;
if (!articolo.get(ANAMAG_CODART).blank())
{
const char reorder_type=articolo.get_char(ANAMAG_RIORDINO);
if (reorder_type!='F' && reorder_type!=' ' && reorder_type!='\0') // e' a riordino
add=FALSE;
}
if (add)
{
// articolo gestito dall'MRP
TString16 mag = line.codmag();
TString16 imp = line.codimp();
TString16 lin = line.codlin();
_artinfo.art2magimpline(art, mag, imp, lin);
TMRP_line* son = _articles.find(art, riga.giacenza(),mag, imp, lin, 0L);
if (son == NULL)
{
son = _articles.find(art, riga.giacenza(), mag, imp, lin, 0L, TRUE);
son->set_description(distinta.describe(art));
}
line.add_son(riga.val(), son);
}
}
}
}
if (pi != NULL) delete pi;
return _articles.items() > 0;
}
/////////// finished: 100%
/////////// tested:
bool TMatResPlanning ::build_sched_receipts(const TMatResMask& m)
{
TDate date_fr = m.get(F_DADATA);
const int bucket_fr = m.round_date(date_fr, FALSE);
TDate date_to = m.get(F_ADATA);
const int bucket_to = m.round_date(date_to, TRUE);
const int year_fr = date_fr.year();
const int year_to = date_to.year();
TTable num("%NUM");
TCodice_numerazione cod;
TRelation rel(LF_DOC);
TCursor cur(&rel);
const TRectype& curr = cur.curr();
TRectype filter_fr(curr), filter_to(curr);
TString_array& n = m.sfield(F_NUM_ORF).rows_array();
TString_array& a = m.sfield(F_TIPI_ORF).rows_array();
TProgind pi(table_items("%NUM"), "Fase 3: caricamento arrivi futuri...", TRUE, TRUE);
// Scandisce tutte le numerazioni considerando solo quelle
// contenenti i tipi documento specificati nella maschera
for (int err = cod.first(num); err == NOERR; err = cod.next(num))
{
pi.addstatus(1);
if (pi.iscancelled())
return FALSE;
if (test_codnum(cod, n))
{
// Filtra il cursore in modo da limitarlo alla numerazione
// corrente ed agli anni specificati dalle due date limite
filter_fr.put(DOC_PROVV, "D");
filter_fr.put(DOC_CODNUM, cod.get("CODTAB"));
filter_fr.put(DOC_ANNO, year_fr);
filter_to.put(DOC_PROVV, "D");
filter_to.put(DOC_CODNUM, cod.get("CODTAB"));
filter_to.put(DOC_ANNO, year_to);
cur.setregion(filter_fr, filter_to);
TString cfilter;
cfilter << DOC_CODNUM << "==" << '"' << cod.get("CODTAB") << '"' ;
cur.setfilter(cfilter);
const long items = cur.items();
cur.freeze(TRUE);
// Scandisce i documenti inevasi e considera solo
// quelli con uno stato nel range corretto
for (cur = 0; cur.pos() < items; ++cur)
{
const bool evaso = curr.get_bool(DOC_DOCEVASO);
if (evaso)
continue;
if (!test_status(curr, a))
continue;
// Data di consegna standard
TDate doc_cons = curr.get(DOC_DATACONS);
if (!doc_cons.ok())
doc_cons = curr.get(DOC_DATADOC);
// Scandisce le righe articolo e memorizza
// le quantita' richieste
TDocumento doc(cur.curr());
for (int r = doc.physical_rows(); r > 0; r--)
{
// Seleziona le righe articolo non ancora evase
const TRiga_documento& riga = doc[r];
TDate consegna = riga.get(RDOC_DATACONS);
if (!consegna.ok())
consegna = doc_cons;
// Data consegna troppo avanti
if (consegna > date_to)
continue;
if (riga.is_articolo())
{
const real qta = riga.qtaresidua();
if (qta > ZERO)
{
const TCodice_articolo art = riga.get(RDOC_CODARTMAG);
const TString16 liv = riga.get(RDOC_LIVELLO);
const TString16 mag = riga.get(RDOC_CODMAG);
const TString16 imp = riga.get(RDOC_IMPIANTO);
const TString16 lin = riga.get(RDOC_LINEA);
TMRP_line* line = _articles.find(art, liv, mag, imp, lin, 0L, FALSE);
if (line != NULL)
{
const TCodice_um um = riga.get(RDOC_UMQTA);
TQuantita q(art, um, qta);
q.convert2umbase();
const TMRP_time t(consegna, 0, imp, lin);
line->add_sched_rec(t, q.val());
}
}
}
}
}
cur.freeze(FALSE);
}
}
return _articles.items() > 0L;
}
/////////// finished: 99%
/////////// tested: 99%
bool TMatResPlanning::preprocess_cycle(const TMatResMask& m)
{
// costruisce il gross requirement dai DOC di planning
if (build_gross_requirements(m))
// esplode l'array mediante la DIBA
if (explode_articles(m))
// costruisce gli sched rec degli elementi dai DOCS ordini fornitore emessi
return build_sched_receipts(m);
return FALSE;
}
/////////// finished: 99%
/////////// tested:
// implementa la logica che porta dal fabbisogno lordo a quello netto
// per quel bucket e riporta il balance on hand sul bucket successivo
bool TMatResPlanning::gross2net_logic(TMRP_line &curr_article, int bucket, bool lotsizing_f, bool lotsizing_p)
{
// Verifico se esiste gia' un fabbisogno netto
real sm,tmpreal = -curr_article.net_requirement(bucket);
if (tmpreal >= ZERO) // Devo calcolare il fabbisogno netto
{
tmpreal = curr_article.on_hand(bucket);
tmpreal -= curr_article.gross_requirement(bucket);
curr_article.scorta_minima(sm, curr_article.time(bucket).date());
tmpreal -= sm;
}
tmpreal += curr_article.sched_receipts(bucket);
// adegua il quantitativo del fabbisogno netto in base al lot-sizing, se il valore risulta negativo
const int n_figli = curr_article.sons();
real net_req;
if ((lotsizing_p && n_figli!=0 )||(lotsizing_f && n_figli==0) )
net_req = curr_article.sizeup_net_requirement(bucket, tmpreal);
else
net_req = curr_article.set_net_req(bucket, -tmpreal);
// calcola la giacenza del bucket successivo
tmpreal += net_req;
if (tmpreal > ZERO)
{
const int nb = curr_article.next_bucket(bucket);
if (nb <= curr_article.last_bucket())
curr_article.set_on_hand(nb, tmpreal);
}
// trasforma i fabbisogni totali nel fabbisogno lordo dei figli
net_req += curr_article.sched_receipts(bucket);
if (net_req > ZERO)
{
TMRP_time lead_time;
curr_article.lead_time(bucket, lead_time);
for (int o = 0; o < n_figli; o++)
{
TMRP_line& article_son = curr_article.son(o);
tmpreal = net_req * curr_article.qta_son(o);
if (tmpreal > ZERO)
article_son.add_gross_req(lead_time, tmpreal);
}
}
return TRUE;
}
/////////// finished: 99%
/////////// tested:
bool TMatResPlanning::net_requirement_cycle(const TMatResMask& m)
{
bool ok = TRUE;
bool lotsizing_p=m.get_bool(F_LOTSIZING_P);
bool lotsizing_f=m.get_bool(F_LOTSIZING_F);
// ordina gli articoli
const long total = _articles.sort();
TProgind pi(total, "Fase 4: calcolo dei fabbisogni netti", FALSE, TRUE);
// cicla iterativamente sugli elementi di _articles
for (long a = 0; a < total; a++)
{
pi.addstatus(1);
TMRP_line& curr_article = _articles[a];
const int last = curr_article.last_bucket();
if (last >= 0)
{
real curgiac;
curr_article.set_on_hand(0, curr_article.giacenza_attuale(curgiac, m.get_date(F_DADATA)));
for (int bucket = 0; ok && bucket <= last; bucket = curr_article.next_bucket(bucket))
{
ok = gross2net_logic(curr_article, bucket, lotsizing_f, lotsizing_p);
if (!ok)
break;
}
}
}
return ok;
}
/////////// finished: 100%
/////////// tested : 100%
// genera le righe degli ordini di produzione o a fornitore
bool TMatResPlanning::build_orders(TMatResMask& m)
{
TSheet_field& sf = m.sfield(F_ORDINI);
sf.destroy();
// TArticolo articolo;
const long tot = _articles.items();
if (tot > 0L)
{
const int leadtime = m.get_int(F_LEADTIME);
TProgind pi(tot, "Fase 5: generazione righe ordini...", FALSE, TRUE);
for (long a = 0; a < tot; a++)
{
pi.addstatus(1);
const TMRP_line& line = _articles[a];
const TRectype& anarec = cache().get(LF_ANAMAG,line.codice());
const TRectype& distrec = cache().get(LF_DIST,line.codice());
const bool prod = distrec.get_bool("ARTPROD");
const long forn = prod ? 0 : anarec.get_long("CODFORN");
for (int b = line.last_bucket(); b >= 0; b--)
{
real qta = line.net_requirement(b);
if (!qta.is_zero() && !qta.round(5).is_zero())// > real("0.00001"))
{
TMRP_time time = line.time(b);
if (line.sons() == 0) // Sottrai alle foglie il bonus
time.sub_time(leadtime);
TDate date = time.date();
m.round_date(date);
//articolo.read(line.codice());
//const char reorder_type=articolo.get_char(ANAMAG_RIORDINO);
//if (prod || reorder_type=='F' || reorder_type==' ' || reorder_type=='\0')
m.add_order_line(date, forn, line, b);
}
}
}
}
const int orders=sf.items();
if (orders==0)
message_box("Nessun ordine da emettere");
real price, qta;
TCodice_articolo codart;
FOR_EACH_SHEET_ROW(sf,n,row)
{
long codcli=atol(row->get(F_FORNITORE-FIRST_FIELD));
const TRectype& rec=cache().get(LF_DIST,row->get(F_ARTICOLO-FIRST_FIELD));
bool art_prod = rec.get_bool("ARTPROD");
bool art_acq = rec.get_bool("ARTACQ") || codcli!=0L;
if (rec.empty()) //
art_prod=!(art_acq=TRUE);
TString16 tipoord=!art_acq ? "P":"F" ;
row->add(tipoord, F_ORD_TYPE-FIRST_FIELD);
sf.check_row(n);
if (tipoord=="P")
tipoord=" ";
else
tipoord=m.get(F_TIPOCF_CONDV);
row->get(F_QUANTITA-FIRST_FIELD,qta);
row->get(F_ARTICOLO-FIRST_FIELD,codart);
find_price(m.get(F_TIPOCV),m.get(F_CODCONDV),m.get(F_CATVEN_CV),
tipoord, codcli,
codart, qta,
price);
row->add(price.string(), F_PREZZO-FIRST_FIELD);
sf.enable_cell(n,F_ORD_TYPE-FIRST_FIELD,art_prod != art_acq);
}
return TRUE;
}
/////////// finished: 90%
/////////// tested : 90%
// genera gli ordini di produzione o a fornitore
bool TMatResPlanning::emit_orders(TMatResMask& m)
{
TSheet_field& sf = m.sfield(F_ORDINI);
TDate today(TODAY);
int docf = 0, docp = 0;
TAssoc_array docs;
TToken_string key;
FOR_EACH_SHEET_ROW(sf, r, row)
{
TToken_string& riga = *row;
if (*riga.get(0) == 'X')
{
const TDate datadoc = riga.get(sf.cid2index(F_DATADOC));
const TDate datacon = riga.get(sf.cid2index(F_DATACONS));
const char type = riga.get_char(sf.cid2index(F_ORD_TYPE));
const bool prod = type == 'P';
const long forn = prod ? 0 : riga.get_long(sf.cid2index(F_FORNITORE));
const real qta(riga.get(sf.cid2index(F_QUANTITA)));
riga.add(" ",sf.cid2index(F_OK));
if (qta.is_zero())
continue;
if (datadoc < today)
if (!noyes_box("Riga %d: data di documento inferiore a quella odierna. Confermi ?",r+1))
continue;
if (datacon < datadoc)
if (!noyes_box("Riga %d: data di consegna inferiore a quella del documento. Confermi ?",r+1))
continue;
if (prod || forn!=0L)
{
key = datadoc.string();
key.add(datacon.string());
key.add(type);
key.add(forn);
TDocumento* doc = (TDocumento*)docs.objptr(key);
if (doc == NULL)
{
const TString& codnum = m.get(prod ? F_NUM_PROD : F_NUM_FORN );
const TString& tipdoc = m.get(prod ? F_TIPO_PROD: F_TIPO_FORN );
doc = new TDocumento('D', datadoc.year(), codnum, 0);
doc->set_tipo(tipdoc);
TRectype& testata = doc->head();
testata.put(DOC_DATADOC, datadoc);
testata.put(DOC_DATACONS, datacon);
if (prod)
docp++;
else
{
testata.put(DOC_TIPOCF, "F");
testata.put(DOC_CODCF, forn);
docf++;
}
docs.add(key, doc);
}
TRiga_documento& rdoc = doc->new_row(m.get(prod ? F_RIGA_PROD : F_RIGA_FORN));
rdoc.put(RDOC_CODART, riga.get(sf.cid2index(F_ARTICOLO)));
TString80 str;
TCodice_livelli & livelli = m.livelli_giacenza();
livelli.pack_grpcode(str, riga.get(sf.cid2index(F_LIV1)), 1);
livelli.pack_grpcode(str, riga.get(sf.cid2index(F_LIV2)), 2);
livelli.pack_grpcode(str, riga.get(sf.cid2index(F_LIV3)), 3);
livelli.pack_grpcode(str, riga.get(sf.cid2index(F_LIV4)), 4);
rdoc.put(RDOC_LIVELLO,str);
add_magcode(str,riga.get(sf.cid2index(F_MAGAZZINO)));
add_depcode(str,riga.get(sf.cid2index(F_DEPOSITO)));
rdoc.put(RDOC_CODMAG, str);
rdoc.put(RDOC_IMPIANTO, riga.get(sf.cid2index(F_CODIMP)));
rdoc.put(RDOC_LINEA, riga.get(sf.cid2index(F_CODLIN)));
rdoc.put(RDOC_QTA, qta);
rdoc.put(RDOC_UMQTA, riga.get(sf.cid2index(F_UM)));
rdoc.put(RDOC_PREZZO, riga.get(sf.cid2index(F_PREZZO)));
TString ddd=riga.get(sf.cid2index(F_DESCART));
rdoc.put(RDOC_DESCR, riga.get(sf.cid2index(F_DESCART)));
riga.add("X",sf.cid2index(F_OK));
}
else
error_box("Codice fornitore mancante alla riga %d", r+1);
}
}
const int tot = int(docs.items());
if (tot > 0 && yesno_box("Confermare la generazione di\n"
"%d ordini di produzione e di\n"
"%d odini a fornitori?", docp, docf))
{
TProgind pi(tot, "Generazione ordini", FALSE, TRUE);
FOR_EACH_ASSOC_OBJECT(docs, hash, str, obj)
{
pi.addstatus(1);
TDocumento* doc = (TDocumento*)obj;
if (doc->write()!=NOERR)
doc->read(); // unlock
}
FOR_EACH_SHEET_ROW_BACK(sf, r, row)
{
if (*row->get(sf.cid2index(F_SELECTED)) == 'X' && *row->get(sf.cid2index(F_OK))=='X')
sf.destroy(r);
}
m.enable(DLG_SAVEREC, sf.items() > 0);
}
return tot > 0;
}
void TMatResPlanning::compute(TMatResMask& m)
{
if (preprocess_cycle(m)) // req iniziale dai docs
if (net_requirement_cycle(m))
build_orders(m);
}
void TMatResPlanning::main_loop()
{
open_files(LF_TABCOM, LF_TAB, LF_DOC, LF_RIGHEDOC, 0);
open_files(LF_CLIFO, LF_CFVEN, LF_OCCAS, LF_INDSP,LF_CONDV, 0);
open_files(LF_ANAMAG, LF_DIST, LF_RDIST, 0);
open_files(LF_MAG, 0);
TMatResMask m;
TConfig prassid(CONFIG_DITTA, "ve"); // apre il file di configurazione della ditta corrente
if (prassid.get_bool("GES", NULL, A_LISTINI))
{
m.enable(F_CATVEN_CV,prassid.get_bool("GESLISCV"));
}
else
{
m.disable(F_TIPOCV);
m.disable(F_CATVEN_CV);
}
if (!prassid.get_bool("GES", NULL, A_CONTRATTI) ||
!prassid.get_bool("GESCONCC"))
m.disable(F_TIPOCF_CONDV);
while (m.run()!=K_QUIT);
}
int mr2100(int argc, char* argv[])
{
TMatResPlanning a;
a.run(argc, argv, "Material Requirements Planning");
return 0;
}