a0a36d6b33
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
1674 lines
46 KiB
C++
Executable File
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;
|
|
}
|
|
|