#include #include #include #include #include #include #include #include "mrplib.h" #include "../mg/mglib.h" #include "../ve/velib.h" #include "../ve/veconf.h" #include "mr2100.h" #include "mr2200a.h" #include "mr2200b.h" const real MAXCAPACITY("999999999.99"); /////////////////////////////////////////////////////////// // triga ordine /////////////////////////////////////////////////////////// #define SORT_BY_CAL -1 // cliente / articolo / impianto-linea #define SORT_BY_CLA -2 // #define SORT_BY_ALC -3 // #define SORT_BY_ACL -4 // #define SORT_BY_LAC -5 // #define SORT_BY_LCA -6 // enum TMSP_mode {_actual_schedule, _stock_break}; enum TMSP_logic {_first_fit_logic, _JIT_logic}; class TMSP_constraint; class TMSP_constraints; class TMSP_mask; class TRiga_articolo : public TToken_string { public: static int order_compare(TSheet_field & s, int i1, int i2, int level); static int order_compare( const TToken_string& r1 , const TToken_string& r2 , int level); static int compare_field(TString &str0, TString &str1,short field_no) ; static int order_compareCAL(TSheet_field & s, int i1, int i2); static int order_compareCLA(TSheet_field & s, int i1, int i2); static int order_compareALC(TSheet_field & s, int i1, int i2); static int order_compareACL(TSheet_field & s, int i1, int i2); static int order_compareLAC(TSheet_field & s, int i1, int i2); static int order_compareLCA(TSheet_field & s, int i1, int i2); static bool find_block(const TSheet_field& sf, const int riga, int &first_row, int &last_row ); int compare(const TToken_string& r, int level) const; TRiga_articolo& operator=(TToken_string& r); TRiga_articolo& operator+=(TRiga_articolo& r); TRiga_articolo() : TToken_string(128) { } TRiga_articolo(const TMSP_constraint & line); virtual ~TRiga_articolo() { } }; /////////////////////////////////////////////////////////// // TMSP_record /////////////////////////////////////////////////////////// class TMSP_record : public TObject { public: long _numdoc; int _numrig; real _qta; real _price; TMSP_record() : _numdoc(0L), _numrig(0) { } }; /////////////////////////////////////////////////////////// // TMSP_record_array; /////////////////////////////////////////////////////////// class TMSP_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); } TMSP_record& operator[](int b); }; /////////////////////////////////////////////////////////// // TCRP_line ; carichi di linea /////////////////////////////////////////////////////////// class TCapacita_prod : public TSortable { real _capacity; // Capacita' macchina real _human_capacity; // Capacita' macchina real _load; // Carico macchina real _human_load; // Carico uomo // static TDecoder* _umart1; // Decoder per trovare unita' base articolo // static TDecoder* _umart2; // Decoder per trovare conversione // static TDecoder* _umdist; // Decoder per trovare unita' base distinta protected: virtual int compare(const TSortable& s) const; virtual TObject* dup() const { return new TCapacita_prod(*this); } void copy(const TCapacita_prod& q); real get_factor(const TCodice_um& um) const; void convert(real& val, const TCodice_um& from_um, const TCodice_um& to_um) const; public: const TCapacita_prod& operator =(const TCapacita_prod& q) { copy(q); return q; } void set(const real& cap, const real& load); void set_capacity(const real& cap){_capacity=cap;} void set_human_capacity(const real& cap){_human_capacity=cap;} void set_load(const real& load) {_load=load;} void set_human_load(const real& load) {_human_load=load;} void add_capacity(const real& cap){_capacity+=cap;} void add_human_capacity(const real& cap){_human_capacity+=cap;} void add_load(const real& load) {_load+=load;} void add_human_load(const real& load) {_human_load+=load;} real & capacity() {return _capacity;} real & load() {return _load;} real & human_capacity() {return _human_capacity;} real & human_load() {return _human_load;} TCapacita_prod (); TCapacita_prod (const real& cap, const real& load); TCapacita_prod (const TCapacita_prod & q) { copy(q); } virtual ~TCapacita_prod () {} }; class TCRP_line : public TSortable { TString8 _lineap, _imp; TString _desc; bool _on_sheet; TArray _bucket; protected: virtual int compare(const TSortable& s) const; TCapacita_prod& bucket(int n) ; TCapacita_prod& operator[](int n) {return bucket(n);} public: const TString& codimp() const { return _imp; } const TString& codlin() const { return _lineap; } const TString& desc() const { return _desc; } void set_desc(const char* str) { _desc = str; } int last() const { return _bucket.last(); } int pred(int i) const { return _bucket.pred(i); } real& capacity(int b) { return bucket(b).capacity(); } real& load(int b) { return bucket(b).load(); } real& human_capacity(int b) { return bucket(b).human_capacity(); } real& human_load(int b) { return bucket(b).human_load(); } void set_capacity(int b,const real & v ) { bucket(b).set_capacity(v); } void set_human_capacity(int b,const real & v ) { bucket(b).set_human_capacity(v); } // void set_load(int b, const real & v) { bucket(b).set_load(v); } // void set_human_capacity(int b,const real & v ) { bucket(b).set_human_capacity(v); } // void set_human_load(int b, const real & v) { bucket(b).set_human_load(v); } // void add_capacity(int b,const real & v ) { bucket(b).add_capacity(v); } void add_load(int b, const real & v) { bucket(b).add_load(v); } // void add_human_capacity(int b,const real & v ) { bucket(b).add_human_capacity(v); } void add_human_load(int b, const real & v) { bucket(b).add_human_load(v); } void fill_capacity_row(TToken_string& row, bool percent=FALSE); void fill_load_row(TToken_string& row, bool percent=FALSE); void fill_hcapacity_row(TToken_string& row, bool percent=FALSE); void fill_hload_row(TToken_string& row, bool percent=FALSE); bool is_on_sheet() const { return _on_sheet; } void set_on_sheet(bool on = TRUE) { _on_sheet = on; } //TCRP_line(const TString& codlin); TCRP_line(const TString& codimp,const TString& codlin); virtual ~TCRP_line() { } }; class TCRP_lines : public TMRP_array { protected: virtual TSortable* new_obj(const TToken_string& key) const; public: TCRP_line* find(const TLinea_prod& linea_prod, const char * codart="", long codcli=0L, bool create=FALSE); TCRP_line* find(const char * codimp="", const char * codlin="", const char * codart="", long codcli=0L, bool create=FALSE); TCRP_line& operator[](long n) const { return (TCRP_line&)find_obj(n); } }; class TMaster_code : public TObject { TString _code; TString _liv; TString _um; TString _codimp; TString _codlin; TString _codmag; real _expr; real _leadtime; public: const char *articolo() const {return _code;} const char *livello() const {return _liv;} const char *um() const {return _um;} const real &expr() const {return _expr;} const real &leadtime() const {return _leadtime;} const char *codimp() const {return _codimp;} const char *codlin() const {return _codlin;} const char *codmag() const {return _codmag;} TMaster_code(const char *code, const char *liv, const char *ummaster, const real &expr, const real &leadtime, const char *codimp, const char *codlin, const char *codmag); TMaster_code(const char *code, const char *liv,const char *ummaster, const real &expr, int leadtime, const char *codimp, const char *codlin, const char *codmag); virtual ~TMaster_code() {} }; class TMSP_constraints : public TMRP_array { protected: virtual TSortable* new_obj(const TToken_string& key) const; public: TMSP_constraint* find(long cliente,const TString& codart,const TString& giac, const TString& imp, const TString& lin, const TString& mag, bool create=FALSE) ; TMSP_constraint& operator[](long n) const { return (TMSP_constraint&)find_obj(n); } }; class TMSP_constraint : public TSortable { long _cliente; TCodice_articolo _codart; TString16 _giac; TString8 _imp, _lin; TString8 _mag; TString _desc; bool _on_sheet; TArray _mastercodes; // codici di livello 2 per articoli NON master TMSP_constraints _upperlines; // vincoli dei codici di livello 1 per articoli master TMSP_record_array _bucket_qta; protected: virtual int compare(const TSortable& s) const; public: long cliente() const { return _cliente; } 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& codmag() const { return _mag; } const TString& desc() const { return _desc; } void set_desc(const char* str) { _desc = str; } bool riga(int b, long& ndoc, int& nriga); void set_riga(int b, long ndoc, int nriga); // master sched. a due livelli: void add_mastercode(TMaster_code * mc); TMaster_code * get_mastercode(int i=0); bool has_mastercodes() const ; void add_upperline(TMSP_constraint& constr); TMSP_constraint* get_upperline(TMSP_constraint& constr); bool has_upperlines() const ; int last() const { return _bucket_qta.last(); } int pred(int i) const { return _bucket_qta.pred(i); } virtual real& qta(int b) { return _bucket_qta[b]._qta; } virtual real& price(int b) { return _bucket_qta[b]._price; } virtual void fill_sheet_row(TToken_string& row, const TMSP_mask & m, bool codes_only=FALSE); bool is_on_sheet() const { return _on_sheet; } void set_on_sheet(bool on = TRUE) { _on_sheet = on; } TMSP_constraint & operator=(const TMSP_constraint & line); TMSP_constraint(long cliente, const TCodice_articolo& codart, const TString& giac, const TString& imp, const TString& lin, const TString& mag); TMSP_constraint(TMSP_constraint & cons); virtual ~TMSP_constraint() { } }; // linee contenenti pianificazioni class TMSP_line : public TMSP_constraint { public: virtual void fill_sheet_row(TToken_string& row, const TMSP_mask & m, bool codes_only=FALSE); TMSP_line(long cliente, const TCodice_articolo& codart, const TString& giac, const TString& mag, const TString& imp, const TString& lin); TMSP_line(TMSP_line & line); TMSP_line(TMSP_constraint & cons); virtual ~TMSP_line() { } }; class TMSP_lines : public TMRP_array { protected: virtual TSortable* new_obj(const TToken_string& key) const; public: TMSP_line* find(long cliente, const TString& art, const TString& gia, const TString& imp, const TString& lin, const TString& mag, bool create=FALSE); TMSP_line* find(const TToken_string& row, TCodice_livelli & levels, bool create=FALSE); TMSP_line& operator[](long i) const { return (TMSP_line&)find_obj(i); } }; class TMSP_mask : public TCalendar_mask { TCodgiac_livelli _livelli; // quantitą provenienti dai documenti "ordine": TMSP_constraints _constraints; // quantitą provenienti dai documenti "planning" (commesse): TMSP_lines _articles; // quantitą per il processo di nettificazione/sizing in caso di generazione dell'MSP TMRP_lines _mrp_articles; // proposte del sistema TMRP_lines _proposed_articles, _proposed_1stlevel; // capacitą linea per linea TCRP_lines _capacities; // albero per le ricerche sulle dist TDistinta_tree _dist_tree; protected: bool on_field_event(TOperable_field& o, TField_event e, long jolly); bool load_MRP_lines(TMSP_mode mode, int level=0); bool nettify_MRP_lines(bool already_net, bool lotsizing=FALSE); void add_MRP_bucket(TMRP_line& new_article, int nbucket, const real &curr_arts); // proposta automatica del sistema bool general_review(bool check_machine, bool check_human, bool useextralines, bool useextrahours,bool lotsizing, bool no_outcapacity, int anticipomin,int anticipomax, TMSP_mode mode, TMSP_logic logic ); bool insert_propose(bool verbose=FALSE) ; bool remove_propose(bool verbose=FALSE) ; void add_or_sub_propose(char sign); void propose_1stJIT(TMSP_logic logic, bool check_m, bool check_h); // void propose_firstfit(bool check_m, bool check_h); // bool firstfit_review(bool check_machine, bool check_human, bool useextralines, bool useextrahours,bool lotsizing, bool no_outcapacity, int anticipomin,int anticipomax, TMSP_mode mode); // void propose_JIT(bool check_m, bool check_h); // bool JIT_review(bool check_machine, bool check_human, bool useextralines, bool useextrahours,bool lotsizing, bool no_outcapacity, int anticipomin,int anticipomax, TMSP_mode mode); // controlli bool sort_sheet(); void check_articles(); void check_capacities(); void pack_article_sheet(); // capacity review bool capacity_review(bool useextralines, bool useextrahours); TCRP_line *compute_capacity(TLinea_prod &lineaprod, bool useextralines, bool useextrahours); TMSP_constraint* find_constraint(const TMSP_line& l,bool force_insert=FALSE); int find_constr_row(const TMSP_constraint& c); bool do_test_art_row(int r, int c, bool signal); bool test_art_row(int r, bool signal=TRUE); bool test_load_row(int r, bool signal=TRUE); int init_bucket0(TArticolo_giacenza &art, int r); void copy_sheet_row(TToken_string & newrow, const TToken_string & row); void clear_sheets(); bool distinta_master(const char *code); int find_distinta_master(const char * code, TString & master, TString & livmaster,TString & um,real & expr, TString & imp,TString & lin,TString & magdep, int fromindex=0); public: const TCodgiac_livelli& livelli_giac() const {return _livelli;} void msprow2codes(TToken_string &row, long &clifor, TCodice_articolo &art , TString & liv, TString & mag, TString & imp, TString & lin, TString & um); void crprow2codes(TToken_string &row, TString & imp, TString & lin, long &clifor, TCodice_articolo &art , TString & liv, TString & um); TMRP_line* find_propose(long codcli, const char * codart,const char * liv, const char * codimp,const char * codlin,const char * mag, bool Force=FALSE); TMSP_line* find_article(long cli, const char *art, const char *liv, const char *imp, const char *lin, const char *mag,bool Force=FALSE) {return _articles.find(cli, art, liv, imp, lin, mag, Force);} int days_per_bucket() const; int round_date(TDate& date, bool up = FALSE) const; void round_field(TMask_field& fld, bool up) const; int test_codnum(const TCodice_numerazione& num) const; bool test_tipodoc_num( const TSheet_field &sheet_num , const TSheet_field &sheet_type) ; bool test_status(const TRectype& doc, int tipo) const; bool carica_documenti(); void fill_sheet(); void salva_documenti(); TMSP_mask(); virtual ~TMSP_mask() { } }; class TMSPCheck_mask : public TAutomask { protected: int _col, _row, _constr_row, _last_col, _last_row; TMSP_mask * _main_mask; TSheet_field * _sheet; protected: int max_rows() {return _sheet->items();} void go_top(); bool on_field_event(TOperable_field& o, TField_event e, long jolly); bool move_to(int dlg); virtual void check_pos_range(); virtual int fill_mask(const bool show=TRUE); virtual bool is_constraint(int row); virtual bool is_article(int row); virtual int find_constraint(int row); public: void gopos(int row, int col); virtual void gopos_mainmask(); void fix_actual_pos(); short last_row(){ return _last_row;} short last_col(){ return _last_col;} TMSPCheck_mask(TMSP_mask * main_mask); TMSPCheck_mask(TMSP_mask * main_mask,const char * m,TSheet_field * s ); }; class TCRPCheck_mask : public TMSPCheck_mask { protected: virtual void check_pos_range(); virtual int fill_mask(const bool show=TRUE); virtual bool is_constraint(int row); virtual bool is_article(int row); virtual int find_constraint(int row); public: TCRPCheck_mask(TMSP_mask * main_mask); }; /////////////////////////////////////////////////////////// TMSP_record& TMSP_record_array::operator[](int b) { CHECKD(b >= 0, "Invalid TMSP_record_array ", b); TMSP_record* rec = (TMSP_record*)_buckets.objptr(b); if (rec == NULL) { rec = new TMSP_record; _buckets.add(rec, b); } return *rec; } /////////////////////////////////////////////////////////// TCapacita_prod::TCapacita_prod () : _capacity(), _load() { } TCapacita_prod::TCapacita_prod (const real& cap, const real& load) : _capacity(cap), _load(load) { } void TCapacita_prod::set(const real& cap, const real& load) { _capacity=(cap);_load=(load);} void TCapacita_prod::copy (const TCapacita_prod & q) { _capacity=q._capacity; // Valore attuale _load=q._load; // Valore attuale } int TCapacita_prod::compare(const TSortable& s) const { TCapacita_prod& c=(TCapacita_prod&)s; real r=_capacity-c._capacity; return r.sign(); } TCRP_line::TCRP_line(const TString& codimp,const TString& codlin) : _lineap(codlin),_on_sheet(FALSE) { if (!codlin.blank()) { TTable tablnp("LNP"); tablnp.put("CODTAB",_lineap); tablnp.read(); _desc = tablnp.get("S0"); _imp = tablnp.get("S6"); } else { _imp = codimp ; if (!codimp.blank()) { TTable tabimp("IMP"); tabimp.put("CODTAB",_imp); tabimp.read(); _desc = tabimp.get("S0"); } else { _desc = "Totale ditta"; } } } int TCRP_line::compare(const TSortable& s) const { const TCRP_line& c = (const TCRP_line&)s; int cmp = _imp.compare(c._imp); if (cmp == 0) { cmp = _lineap.compare(c._lineap); } return cmp; } // carica una riga di sheet con i dati dell'oggetto vincolo void TCRP_line::fill_capacity_row(TToken_string& row, bool percent) { row.add(codimp(),F_CODIMPCRP -FIRST_FIELD); row.add(codlin(),F_CODLINCRP -FIRST_FIELD); row.add(percent ? "%": "ore",F_LUM -FIRST_FIELD); real r; for (int bu = last(); bu > 0; bu = pred(bu)) { r=capacity(bu); if (!r.is_zero()) row.add(percent ? "100" : r.string(), bu + F_LBUCKET0 -FIRST_FIELD); // buckets else row.add("", bu + F_LBUCKET0 -FIRST_FIELD); } } void TCRP_line::fill_hcapacity_row(TToken_string& row, bool percent) { row.add(codimp(),F_CODIMPCRP -FIRST_FIELD); row.add(codlin(),F_CODLINCRP -FIRST_FIELD); row.add(percent ? "%": "ore",F_LUM -FIRST_FIELD); real r; for (int bu = last(); bu > 0; bu = pred(bu)) { r=human_capacity(bu); if (!r.is_zero()) row.add(percent ? "100" : r.string(), bu + F_LBUCKET0 -FIRST_FIELD); // buckets else row.add("", bu + F_LBUCKET0 -FIRST_FIELD); } } // carica una riga di sheet con i dati dell'oggetto vincolo void TCRP_line::fill_load_row(TToken_string& row, bool percent) { row.add(codimp(),F_CODIMPCRP -FIRST_FIELD); row.add(codlin(),F_CODLINCRP -FIRST_FIELD); row.add(percent ? "%": "ore",F_LUM -FIRST_FIELD); real r; for (int bu = last(); bu > 0; bu = pred(bu)) { r=percent ? (capacity(bu).is_zero() ? ZERO : (100L * load(bu) / capacity(bu))) : load(bu) ; row.add(r.string(), bu + F_LBUCKET0 -FIRST_FIELD); // buckets } } void TCRP_line::fill_hload_row(TToken_string& row, bool percent) { row.add(codimp(),F_CODIMPCRP -FIRST_FIELD); row.add(codlin(),F_CODLINCRP -FIRST_FIELD); row.add(percent ? "%": "ore",F_LUM -FIRST_FIELD); real r; for (int bu = last(); bu > 0; bu = pred(bu)) { r=percent ? (human_capacity(bu).is_zero() ? ZERO :(100L * human_load(bu) / human_capacity(bu))) : human_load(bu) ; row.add(r.string(), bu + F_LBUCKET0 -FIRST_FIELD); // buckets } } TCapacita_prod& TCRP_line::bucket(int n) { TCapacita_prod *o; o=(TCapacita_prod *)_bucket.objptr(n); if (o==NULL) { o=new TCapacita_prod(); _bucket.add(o,n); } return *o; } /////////////////////////////////////////////////////////// // TCRP_lines ; carichi delle varie linee /////////////////////////////////////////////////////////// TSortable* TCRP_lines::new_obj(const TToken_string& key) const { TString8 codimp, codlin; key.get(0,codimp); key.get(1,codlin); return new TCRP_line(codimp, codlin); } TCRP_line* TCRP_lines::find(const TLinea_prod& linea_prod, const char * codart, long codcli, bool create) { /* _key=linea_prod.codimp(); _key.add(linea_prod.codice()); TSortable* s = create ? add_obj(_key) : find_obj(_key); return (TCRP_line*)s;*/ return find(linea_prod.codimp(), linea_prod.codice(),codart, codcli, create); } TCRP_line* TCRP_lines::find(const char * codimp, const char * codlin, const char * codart, long codcli, bool create) { _key.add(codimp,0); _key.add(codlin,1); _key.add(codart,2); // articolo _key.add(format("%ld",codcli),3); // cliente TSortable* s = create ? add_obj(_key) : find_obj(_key); return (TCRP_line*)s; } /////////////////////////////////////////////////////////// // TMaster_code /////////////////////////////////////////////////////////// TMaster_code::TMaster_code(const char *code, const char *liv,const char *ummaster, const real &expr, const real &leadtime, const char *codimp, const char *codlin, const char *codmag) : _code(code), _liv (liv), _um(ummaster), _expr(expr), _leadtime(leadtime), _codimp(codimp), _codlin(codlin), _codmag(codmag) { } TMaster_code::TMaster_code(const char *code, const char *liv,const char *ummaster, const real &expr, int leadtime, const char *codimp, const char *codlin, const char *codmag) : _code(code), _liv (liv), _um(ummaster), _expr(expr), _leadtime(format("%d",leadtime)), _codimp(codimp), _codlin(codlin), _codmag(codmag) { } /////////////////////////////////////////////////////////// // TMSP_constraint /////////////////////////////////////////////////////////// int TMSP_constraint::compare(const TSortable& s) const { const TMSP_constraint& c = (const TMSP_constraint&)s; const long diff = _cliente - c._cliente; int cmp = diff == 0L ? 0 : (diff > 0 ? +1 : -1); if (cmp == 0) { 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 = _mag.compare(c._mag); } } } return cmp; } // carica una riga di sheet con i dati dell'oggetto vincolo void TMSP_constraint::fill_sheet_row(TToken_string& row, const TMSP_mask & m, bool codes_only) { const TCodgiac_livelli& livelli=m.livelli_giac(); TString tipocf(m.get(F_TIPOCF)); row.add(m.get_bool(F_NOCLI_IN) ? "" : tipocf,F_TIPOCF_SHEET-FIRST_FIELD); row.add(format("%ld", cliente()),F_CLIENTE-FIRST_FIELD); row.add(codice(),F_ARTICOLO-FIRST_FIELD); row.add(" ",F_PRIORITA-FIRST_FIELD); const TString& liv = livgiac(); for (int l = 1; l <= 4; l++) { if (livelli.enabled(l)) row.add(livelli.unpack_grpcode(liv,l),F_LIV1+l-1-FIRST_FIELD); else row.add(" ",F_LIV1+l-1-FIRST_FIELD); } row.add(desc(),F_DESCART-FIRST_FIELD); row.add(codimp(),F_CODIMP-FIRST_FIELD); row.add(codlin(),F_CODLIN-FIRST_FIELD); TString8 str = _mag.left(3); row.add(str, F_MAGAZZINO-FIRST_FIELD); str = _mag.mid(3); row.add(str,F_DEPOSITO-FIRST_FIELD); const TCodice_um um; const TQuantita qta_art(codice(), um, ZERO); row.add(qta_art.um(),F_UM-FIRST_FIELD); if (codes_only) return; real needs=ZERO; const int last_buck= last(); for (int bu = 0; bu <= last_buck; bu ++) { needs=qta(bu); row.add(needs.string(), bu*2+F_BUCKET0-FIRST_FIELD); needs=price(bu); row.add(needs.string(), bu*2+F_BUCKET0+1-FIRST_FIELD); } row.add(has_mastercodes() ? "M" : " ",F_MASTERCODE-FIRST_FIELD); } bool TMSP_constraint::riga(int buck, long& ndoc, int& nrig) { TMSP_record& b = _bucket_qta[buck]; ndoc = b._numdoc; nrig = b._numrig; return ndoc > 0L && nrig > 0; } void TMSP_constraint::set_riga(int buck, long ndoc, int nrig) { TMSP_record& b = _bucket_qta[buck]; b._numdoc = ndoc; b._numrig = nrig; } bool TMSP_constraint::has_mastercodes() const { return _mastercodes.items()>0; } void TMSP_constraint::add_mastercode(TMaster_code *mc) { _mastercodes.add(mc); } TMaster_code *TMSP_constraint::get_mastercode(int i) { if (i>=0 && i < _mastercodes.items()); return (TMaster_code *) _mastercodes.objptr(i); return NULL; } bool TMSP_constraint::has_upperlines() const { return _upperlines.items()>0; } void TMSP_constraint::add_upperline(TMSP_constraint& mc) { if (_upperlines.items()==0) { // aggiunge se stesso TMSP_constraint * myself=_upperlines.find(cliente(), codice(), livgiac(), codimp(),codlin(),codmag(),TRUE); *myself=*this; } _upperlines.find(mc.cliente(), mc.codice(), mc.livgiac(), mc.codimp(),mc.codlin(),mc.codmag(),TRUE); } TMSP_constraint* TMSP_constraint::get_upperline(TMSP_constraint& mc) { return _upperlines.find(mc.cliente(), mc.codice(), mc.livgiac(), mc.codimp(),mc.codlin(),mc.codmag(),FALSE); } TMSP_constraint & TMSP_constraint::operator=(const TMSP_constraint & line) { _cliente=line.cliente(); _codart=line.codice(); _giac=line.livgiac(); _imp=line.codimp(); _lin=line.codlin(); _mag=line.codmag(); _on_sheet=_on_sheet; _desc=line.desc(); _bucket_qta=line._bucket_qta; return *this; } TMSP_constraint::TMSP_constraint(long cliente, const TCodice_articolo& codart, const TString& giac, const TString& imp, const TString& lin, const TString& mag) : _cliente(cliente), _codart(codart), _giac(giac), _imp(imp), _lin(lin), _mag(mag),_on_sheet(FALSE) { } TMSP_constraint::TMSP_constraint(TMSP_constraint & line) : _cliente(line.cliente()), _codart(line.codice()), _giac(line.livgiac()), _imp(line.codimp()), _lin(line.codlin()), _mag(line.codmag()), _on_sheet(FALSE), _desc(line.desc()) { } /////////////////////////////////////////////////////////// // TMSP_constraints /////////////////////////////////////////////////////////// TSortable* TMSP_constraints::new_obj(const TToken_string& key) const { long cli; key.get(0, cli); TCodice_articolo art; key.get(1, art); art.trim(); TString16 gia; key.get(2, gia); gia.trim(); TString8 imp; key.get(3, imp); imp.trim(); TString8 lin; key.get(4, lin); lin.trim(); TString8 mag; key.get(5, mag); mag.trim(); return new TMSP_constraint(cli, art, gia, imp, lin, mag); } TMSP_constraint* TMSP_constraints::find(long cliente, const TString& art, const TString& gia, const TString& imp, const TString& lin, const TString& mag, bool create) { _key.format("%ld", cliente); _key.add(art);_key.trim(); _key.add(gia);_key.trim(); _key.add(imp);_key.trim(); _key.add(lin);_key.trim(); _key.add(mag);_key.trim(); TSortable* s = create ? add_obj(_key) : find_obj(_key); TMSP_constraint *c=(TMSP_constraint*)s; if (create) { if (c->desc().blank()) c->set_desc(cache().get(LF_ANAMAG,art).get("DESCR")); } return c; } ////////////////////////////////////////////////////////// // TMSP_line /////////////////////////////////////////////////////////// void TMSP_line::fill_sheet_row(TToken_string& row, const TMSP_mask & m, bool codes_only) { TMSP_constraint::fill_sheet_row(row, m, codes_only); row.add(m.get(F_TIPOCF),F_TIPOCF_SHEET-FIRST_FIELD); } TMSP_line::TMSP_line(long cliente, const TCodice_articolo& codart, const TString& giac, const TString& imp, const TString& lin, const TString& mag) : TMSP_constraint(cliente, codart, giac, imp, lin,mag) { } TMSP_line::TMSP_line(TMSP_constraint & cons) :TMSP_constraint(cons) { } TMSP_line::TMSP_line(TMSP_line & line) :TMSP_constraint((TMSP_constraint &)line) { } /////////////////////////////////////////////////////////// // TMSP_lines /////////////////////////////////////////////////////////// TSortable* TMSP_lines::new_obj(const TToken_string& key) const { long cliente; key.get(0, cliente); TCodice_articolo art; key.get(1, art); art.trim(); TString gia; key.get(2, gia); gia.trim(); TString imp; key.get(3, imp); imp.trim(); TString lin; key.get(4, lin); lin.trim(); TString mag; key.get(5, mag); mag.trim(); return new TMSP_line(cliente, art, gia, imp, lin, mag); } TMSP_line* TMSP_lines::find(long cliente, const TString& art, const TString& gia, const TString& imp, const TString& lin, const TString& mag, bool create) { _key.format("%ld", cliente); _key.add(art); _key.add(gia); _key.add(imp); _key.add(lin); _key.add(mag); TSortable* s = create ? add_obj(_key) : find_obj(_key); return (TMSP_line*)s; } TMSP_line* TMSP_lines::find(const TToken_string& row, TCodice_livelli & livelli ,bool create) { TString80 str,giaclev; _key.cut(0); for (int i = 0; i <= 11; i++) { row.get(i, str); str.trim(); switch (i+FIRST_FIELD) { case F_LIV1: case F_LIV2: case F_LIV3: livelli.pack_grpcode(giaclev, str, i+FIRST_FIELD-F_LIV1+1); break; case F_LIV4: livelli.pack_grpcode(giaclev, str, 4); giaclev.trim(); _key.add(giaclev); break; // Concatena i livelli case F_MAGAZZINO: _key.add(str); row.get(i+1, str); str.trim(); _key << str; break; // Concatena magazzini e depositi case F_CLIENTE: if (str.blank()) str = "0"; case F_ARTICOLO: case F_CODIMP: case F_CODLIN: _key.add(str); break; // Aggiungi normalmente } } TSortable* s = create ? add_obj(_key) : find_obj(_key); return (TMSP_line*)s; } /////////////////////////////////////////////////////////// // TPlan_docs /////////////////////////////////////////////////////////// class TPlan_docs : public TMRP_array { TString16 _num, _doc, _rig; protected: virtual TSortable* new_obj(const TToken_string& key) const; public: TDocumento& find(int anno, long num); TRiga_documento& add_to_row(int anno, long num, int riga, const real& qta); long flush(const TDate& data); TPlan_docs(const char* num, const char* tip, const char* rig); virtual ~TPlan_docs() { } }; TSortable* TPlan_docs::new_obj(const TToken_string& key) const { int anno; key.get(0, anno); long num; key.get(1, num); TDocumento* doc = new TDocumento('D', anno, _num, num); doc->head().put(DOC_TIPODOC, _doc); return doc; } TDocumento& TPlan_docs::find(int anno, long num) { _key.format("%d|%ld", anno, num); return *(TDocumento*)add_obj(_key); } TRiga_documento& TPlan_docs::add_to_row(int anno, long num, int riga, const real& qta) { TDocumento& doc = find(anno, num); if (riga <= 0 || riga > doc.physical_rows()) { TRiga_documento& r = doc.new_row(_rig); riga = r.get_int(RDOC_NRIGA); } TRiga_documento& rdoc = doc[riga]; real val = rdoc.get(RDOC_QTA); val += qta; if (val <= ZERO) rdoc.put(RDOC_QTA, ZERO); else rdoc.put(RDOC_QTA, val); return rdoc; } long TPlan_docs::flush(const TDate& data) { const long tot = items(); if (tot > 0L) { TString msg; msg << "Registrazione dei documenti di planning per il " << data; TProgind pi(tot, msg, FALSE, TRUE); TLocalisamfile lfdoc(LF_DOC); for (long d = 0; d < tot; d++) { pi.addstatus(1); TDocumento& doc = (TDocumento&)find_obj(d); for (int r = doc.physical_rows(); r > 0; r--) { const TRiga_documento& riga = doc[r]; if (riga.is_articolo() && riga.quantita() == ZERO) doc.destroy_row(r, TRUE); } if (doc.physical_rows()) doc.write(lfdoc); else doc.remove(lfdoc); } destroy(); } return tot; } TPlan_docs::TPlan_docs(const char* num, const char* tip, const char* rig) : _num(num), _doc(tip), _rig(rig) { } /////////////////////////////////////////////////////////// // ritorna se tutto OK bool TRiga_articolo::find_block(const TSheet_field& sf, const int riga, int &first_row, int &last_row ) { const int narticoli=sf.items(); if (narticoli<=riga) return FALSE; // Calcola l'indice c della riga coi vincoli const int b1 = sf.cid2index(F_BUCKET1); for (first_row = riga; first_row > 0; first_row--) { if (sf.cell_disabled(first_row, b1)) break; } if (first_row+1 < narticoli && sf.cell_disabled(first_row+1, b1)) first_row++; // Calcola l'indice r dell'ultima riga articoli last_row = riga; if (last_row <= first_row) last_row=first_row+1; for ( ; last_row < narticoli-1; last_row++) { if (sf.cell_disabled(last_row+1, b1)) break; } return TRUE; } int TRiga_articolo::order_compare( const TToken_string &r1 , const TToken_string &r2 , int level) { TString16 str0, str1; int cmp = 0; if (level>=0) { for (int i = 2; i <= level && cmp == 0; i++) { r1.get(i, str0); r2.get(i, str1); cmp=TRiga_articolo::compare_field(str0,str1,short(i+FIRST_FIELD)); } } else { // ordinamenti non standard short f; short fields_ACL[] = {F_PRIORITA, F_ARTICOLO, F_CLIENTE, F_SORTCODIMP, F_SORTCODLIN}; short fields_ALC[] = {F_PRIORITA, F_ARTICOLO, F_SORTCODIMP, F_SORTCODLIN, F_CLIENTE}; short fields_LAC[] = {F_PRIORITA, F_SORTCODIMP, F_SORTCODLIN, F_ARTICOLO, F_CLIENTE}; short fields_LCA[] = {F_PRIORITA, F_SORTCODIMP, F_SORTCODLIN, F_CLIENTE, F_ARTICOLO}; short fields_CLA[] = {F_PRIORITA, F_CLIENTE, F_SORTCODIMP, F_SORTCODLIN, F_ARTICOLO}; short fields_CAL[] = {F_PRIORITA, F_CLIENTE, F_ARTICOLO, F_SORTCODIMP, F_SORTCODLIN}; for (int i = 0; i < 5 && cmp == 0; i++) { switch (level) { case SORT_BY_CAL: f=fields_CAL[i]; break; case SORT_BY_CLA: f=fields_CLA[i]; break; case SORT_BY_LCA: f=fields_LCA[i]; break; case SORT_BY_LAC: f=fields_LAC[i]; break; case SORT_BY_ACL: f=fields_ACL[i]; break; case SORT_BY_ALC: f=fields_ALC[i]; break; default: NFCHECK("Ordinamento sconosciuto"); break; } r1.get(f-FIRST_FIELD, str0); r2.get(f-FIRST_FIELD, str1); cmp=TRiga_articolo::compare_field(str0,str1,f); } } return cmp; } // aggiunge li codici di linea/impianto se non presenti usando i default sulle lavorazioni critiche // cio' permette di ordinare per impianto anche le linee di ordine che non hanno solitamente // questa indicazione ma solo il codice articolo void complete_codlinea(TToken_string &r2, bool ignore_imp) { static TDistinta_tree *_dist_tree=NULL; TString8 swp; r2.get(F_SORTCODLIN-FIRST_FIELD,swp); if (swp.blank()) { r2.get(F_CODLIN-FIRST_FIELD,swp); if (swp.blank()) { if (_dist_tree==NULL) _dist_tree= new TDistinta_tree(); if (_dist_tree->set_root(r2.get(F_ARTICOLO-FIRST_FIELD))) { TString codimp(r2.get(F_CODIMP-FIRST_FIELD)); TArray labors; TRiga_esplosione * l=_dist_tree->first_critical_labor(labors); if (l) { TLavorazione *curr_labor=TDistinta_tree::find_labor(l); CHECK(curr_labor,"Lavorazione non trovata"); for (int nlinea=0; nlinea linee_standard(); nlinea ++) { const TRectype &linea_prod= cache().get("LNP",curr_labor->cod_linea(nlinea)); if ( ignore_imp || codimp.blank() || codimp==linea_prod.get("S6")) { r2.add(linea_prod.get("CODTAB"), F_SORTCODLIN-FIRST_FIELD); r2.add(linea_prod.get("S6"), F_SORTCODIMP-FIRST_FIELD); return; } } } } r2.add('\255', F_SORTCODLIN-FIRST_FIELD); } else { r2.add(swp, F_SORTCODLIN-FIRST_FIELD); r2.get(F_CODLIN-FIRST_FIELD,swp); r2.add(swp, F_SORTCODIMP-FIRST_FIELD); } } } int TRiga_articolo::order_compare(TSheet_field & s, int i1, int i2, int level) { TMask &m=s.mask(); const bool ascending=!m.get_bool(F_MSP_SORT_ORDER); int first1, first2, last1, last2; if (find_block(s, i1, first1, last1 )) first1--; if (find_block(s, i2, first2, last2 )) first2--; const TToken_string &r1 = s.row(i1); const TToken_string &r2 = s.row(i2); if (s.cell_enabled(i1, F_BUCKET1-FIRST_FIELD) && s.cell_enabled(i2, F_BUCKET1-FIRST_FIELD) ) { // confronto tra righe dello stesso vincolo if (first1 == first2 ) return order_compare( r1 , r2 , level) * (ascending ? 1 : -1); } else { if (first1 != first2 && first1==i1 && first2==i2 ) { TToken_string &blockr1=(TToken_string &)r1; TToken_string &blockr2=(TToken_string &)r2;; complete_codlinea(blockr1, m.get_bool(F_NOIMP_IN)); complete_codlinea(blockr2, m.get_bool(F_NOIMP_IN)); // confronto tra blocchi ed eventuale swap direttamente qui nella compare int cmp = order_compare(blockr1 , blockr2 , level) * (ascending ? 1 : -1); if (cmp > 0) { for (int bubble=first2; bubble<=last2; bubble++) { for (int r=bubble; r>bubble+first1-first2; r--) { s.swap_rows(r, r-1); } } } } } return 0; } int TRiga_articolo::order_compareCAL(TSheet_field & s, int i1, int i2) { return TRiga_articolo::order_compare(s, i1, i2,SORT_BY_CAL);} int TRiga_articolo::order_compareCLA(TSheet_field & s, int i1, int i2) { return TRiga_articolo::order_compare(s, i1, i2,SORT_BY_CLA);} int TRiga_articolo::order_compareALC(TSheet_field & s, int i1, int i2) { return TRiga_articolo::order_compare(s, i1, i2,SORT_BY_ALC);} int TRiga_articolo::order_compareACL(TSheet_field & s, int i1, int i2) { return TRiga_articolo::order_compare(s, i1, i2,SORT_BY_ACL);} int TRiga_articolo::order_compareLAC(TSheet_field & s, int i1, int i2) { return TRiga_articolo::order_compare(s, i1, i2,SORT_BY_LAC);} int TRiga_articolo::order_compareLCA(TSheet_field & s, int i1, int i2) { return TRiga_articolo::order_compare(s, i1, i2,SORT_BY_LCA);} int TRiga_articolo::compare_field(TString &str0, TString &str1,short field_no) { int cmp; switch (field_no) { case F_CLIENTE: { const long f0 = atol(str0); const long f1 = atol(str1); cmp = f0 == f1 ? 0 : (f0 > f1 ? +1 : -1); } break; case F_PRIORITA: cmp = -str0.compare(str1); break; default: cmp = str0.compare(str1); break; } return cmp; } TRiga_articolo& TRiga_articolo::operator=(TToken_string& r) { this->TToken_string::operator=(r); return *this; } TRiga_articolo& TRiga_articolo::operator+=(TRiga_articolo& r) { NFCHECK( "Can't add incompatible order line"); return *this; } TRiga_articolo::TRiga_articolo(const TMSP_constraint& line) : TToken_string(128) { format("%ld", line.cliente()); add(line.codice(),F_CLIENTE-FIRST_FIELD); add(" ",F_PRIORITA-FIRST_FIELD); const TString& liv = line.livgiac(); for (int l = 1; l <= 4; l++) { if (livelli_giac().enabled(l)) add(livelli_giac().unpack_grpcode(liv,l),F_LIV1+l-1-FIRST_FIELD); else add(" ",F_LIV1+l-1-FIRST_FIELD); } add(line.desc(),F_DESCART-FIRST_FIELD); add(line.codimp(),F_CODIMP-FIRST_FIELD); add(line.codlin(),F_CODLIN-FIRST_FIELD); TString8 str = line.codmag().left(3); add(str, F_MAGAZZINO-FIRST_FIELD); str = line.codmag().mid(3); add(str, F_DEPOSITO-FIRST_FIELD); const TCodice_um um; const TQuantita qta_art(line.codice(), um, ZERO); add(qta_art.um(),F_UM-FIRST_FIELD); TMSP_constraint& lin=(TMSP_constraint& )line; const int last_buck= line.last(); for (int bu = 0; bu <= last_buck; bu ++) { add(lin.qta(bu).string(), bu*2+F_BUCKET0-FIRST_FIELD); } } /////////////////////////////////////////////////////////// // Maschera principale /////////////////////////////////////////////////////////// // Arrotonda la data al bucket e ne restituisce il numero (data iniziale==bucket 0) int TMSP_mask::round_date(TDate& date, bool up) const { // Dimensione del bucke in giorni int bucket_size = days_per_bucket(); // 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 TMSP_mask::round_field(TMask_field& fld, bool up) const { TDate date = fld.get(); if (date.ok()) { round_date(date, up); fld.set(date); } } int TMSP_mask::test_codnum(const TCodice_numerazione& num) const { int r=0; for (int s = 2; s > 0; s--) { const short id = s == 2 ? F_NUM_PLA : F_NUM_ORC; TString_array& nums = sfield(id).rows_array(); for (int i = nums.items()-1; i >= 0; i--) { const char* t = nums.row(i).get(0); if (num.codice() == t) { r|=s; break; } } } return r; } bool TMSP_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) ; const char * cazzoinculo=(const char * )tipo; 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("Il tipo '%s' non appartiene a nessuna delle numerazioni scelte",(const char * )tipo); } return TRUE; } bool TMSP_mask::test_status(const TRectype& doc, int s) const { const short id = (s & 2) ? F_TIPI_PLA : F_TIPI_ORC; TSheet_field& sf = sfield(id); const int idfr = sf.cid2index(F_DASTATO); const int idto = sf.cid2index(F_ASTATO); const TString16 tipodoc = doc.get(DOC_TIPODOC); const int statodoc = doc.get_int(DOC_STATO); bool yes = FALSE; TString_array& a = sf.rows_array(); 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(idfr); const int state_to = riga.get_int(idto); yes = statodoc >= state_fr && statodoc <= state_to; } } return yes; } // Metodo standard per contare gli elementi di una tabella // in attesa di tempi migliori per le librerie /* static long table_items(const char* tab) { TRelation tabrel(tab); TCursor tabcur (&tabrel); long tot = tabcur.items(); return tot; } */ bool TMSP_mask::carica_documenti() { const bool ignore_mag=get_bool(F_NOMAG_IN); const bool ignore_imp=get_bool(F_NOIMP_IN); const bool ignore_lin=get_bool(F_NOLIN_IN); const bool ignore_cli=get_bool(F_NOCLI_IN); const bool ignore_allcli=get_bool(F_NOCLI_OUT); const bool is_master_sched = get_bool(F_MSCHEDULEPLAN); const bool two_level = get_bool(F_2LEVEL_MSP); TDate date_fr = get(F_DADATA); const int bucket_fr = round_date(date_fr, FALSE); TDate date_to = get(F_ADATA); const int bucket_to = round_date(date_to, TRUE); const int year_fr = date_fr.year(); const int year_to = date_to.year(); const TString& numplan = get(F_NUM_PLAN); 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); _articles.destroy(); _constraints.destroy(); _proposed_articles.destroy(); // 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)) { const int tn = test_codnum(cod); if (tn) { // 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 TString msg; msg.format("Caricamento documenti %s (numerazione '%s')",tn & 1 ? "ordine":"di planning",(const char *)cod.codice()); TProgind pi(items,msg , TRUE, TRUE); for (cur = 0; cur.pos() < items; ++cur) { pi.addstatus(1); if (pi.iscancelled()) return FALSE; const bool evaso = curr.get_bool(DOC_DOCEVASO); if (evaso) continue; // Calcola data di consegna per righe che non ce l'hanno TDate datacons = curr.get(DOC_DATACONS); if (!datacons.ok()) datacons = curr.get(DOC_DATADOC); if (!test_status(curr, tn)) continue; // 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]; if (riga.is_articolo()) { const real qta = riga.qtaresidua(); if (qta > ZERO) { const TCodice_articolo art = riga.get(RDOC_CODARTMAG); const TString16 liv = _livelli.enabled() ? riga.get(RDOC_LIVELLO) : ""; const long cli = (ignore_cli && (tn & 1)) ||ignore_allcli ? 0 : doc.get_long(DOC_CODCF) ; const TString16 mag = ignore_mag && (tn & 1) ? "" : riga.get(RDOC_CODMAG); const TString16 imp = ignore_imp && (tn & 1) ? "" : riga.get(RDOC_IMPIANTO); const TString16 lin = ignore_lin && (tn & 1) ? "" : riga.get(RDOC_LINEA); const TCodice_um um = riga.get(RDOC_UMQTA); TQuantita q(art, um, qta); q.convert2umbase(); TDate consegna = riga.get(RDOC_DATACONS); if (!consegna.ok()) consegna = datacons; int buck = round_date(consegna) - bucket_fr + 1; if (buck < 0) buck = 0; else if (buck > LAST_BUCKET) buck = LAST_BUCKET; TMSP_constraint* line; if (tn & 1) { line = _constraints.find(cli, art, liv, imp, lin, mag, TRUE); if (two_level && !distinta_master(art)) { TString master, ummaster, livmaster,impmaster(imp), linmaster(lin),magmaster; real expr; if (!line->has_mastercodes()) { int i=0; while ((i=find_distinta_master(art, master, livmaster, ummaster, expr, impmaster, linmaster, magmaster,i)+1) >0) { TMSP_constraint*masterline=_constraints.find(cli, master , livmaster, imp, lin, mag, TRUE); masterline->add_upperline(*line); line->add_mastercode(new TMaster_code (master, livmaster, ummaster, expr, cache().get(LF_ANAMAG,master).get_real(ANAMAG_GIORNIRIOR), imp, lin, mag)); } } } } else { line = _articles. find(cli, art, liv, imp, lin, mag, TRUE); } if (line->desc().empty()) line->set_desc(riga.get(RDOC_DESCR)); if (buck > 0 && buck < LAST_BUCKET && cod.codice() == numplan) { long numdoc; int numrig; if (!line->riga(buck, numdoc, numrig)) { numdoc = riga.get_long(RDOC_NDOC); numrig = riga.get_int(RDOC_NRIGA); line->set_riga(buck, numdoc, numrig);// memorizza la provenienza dal doc } } line->qta(buck) += q.val(); real price; find_price(get(F_TIPOCV),get(F_CODCONDV),get(F_CATVEN_CV), get(F_TIPOCF), cli, art, line->qta(buck), price); line->price(buck) = price; } } } } cur.freeze(FALSE); } } return _constraints.items() > 0L || _articles.items() > 0L; } // cerca il vincolo "migliore" secondo la logica : // - stesso magazzino , impianto e linea // - stesso magazzino e impianto // - stesso magazzino // - stesso impianto e linea // - stessa linea TMSP_constraint* TMSP_mask::find_constraint(const TMSP_line& l, bool force_insert) { const bool ignore_mag=get_bool(F_NOMAG_IN); const bool ignore_imp=get_bool(F_NOIMP_IN); const bool ignore_lin=get_bool(F_NOLIN_IN); const bool ignore_cli=get_bool(F_NOCLI_IN); TString16 codmag=ignore_mag ? "" : l.codmag(); TString16 codimp=ignore_imp ? "" : l.codimp(); TString16 codlin=ignore_lin ? "" : l.codlin(); long codcli=ignore_cli ? 0L : l.cliente() ; // se il magazzino o l'impianto non e' indicato sul vincolo cerca di individuarlo // in base ai default di linea/impianto if (codimp.blank()) { if (!codlin.blank()) { TLinea_prod &linea_prod=*::get_linea(codlin); codimp=linea_prod.codimp(); } } if (codmag.blank()) { if (!codlin.blank()) { TLinea_prod &linea_prod=*::get_linea(codlin); codmag=linea_prod.codmagdep(); } else if (!codimp.blank()) { TImpianto &impianto=*::get_impianto(codimp); codmag=impianto.codmagdep(); } } // cerca il vincolo con - stesso magazzino , impianto e linea TMSP_constraint* c = _constraints.find(codcli, l.codice(), l.livgiac(), codimp, codlin, codmag); if (c == NULL ) { if (!codmag.blank()) { // cerca il vincolo con - stesso magazzino e impianto c =_constraints.find(codcli, l.codice(), l.livgiac(), codimp, "", codmag); if (c == NULL) // cerca il vincolo con - stesso magazzino c =_constraints.find(codcli, l.codice(), l.livgiac(), "", "", codmag); } if (c == NULL) // cerca il vincolo con - stesso impianto e linea c =_constraints.find(codcli, l.codice(), l.livgiac(), codimp, codlin, ""); if (c == NULL) // cerca il vincolo con - stessa impianto c =_constraints.find(codcli, l.codice(), l.livgiac(), codimp, "" , ""); } if (c == NULL && force_insert) { // MA STO VINCOLO MANCA PROPRIO! ALLORA LO INSERISCO c = _constraints.find(codcli, l.codice(), l.livgiac(), codimp, codlin, codmag, TRUE); if (c->desc().blank()) c->set_desc(l.desc()); } return c; } int TMSP_mask::find_constr_row(const TMSP_constraint& c) { TSheet_field& sf = sfield(F_ARTICOLI); const int maxart=sf.items(); for (int r=0; r 0 && b < LAST_BUCKET) { sf.enable_column(F_BUCKET0 + b*2, d < date_to); sf.enable_column(F_BUCKET0 + b*2+1, d < date_to); sl.enable_column(F_LBUCKET0 + b, d < date_to); //su.enable_column(F_LBUCKET0 + b, d < date_to); } str = d.string(); switch(b) { case 0: str.insert("< "); break; //case 9: /* Nothing!! */ ; break; case LAST_BUCKET: str.insert(">= "); break; default: d += bucket_size; break; } sf.set_column_header(F_BUCKET0 + b*2, str); sf.set_column_header(F_BUCKET0 + b*2 +1, str); sl.set_column_header(F_LBUCKET0 + b, str); //su.set_column_header(F_LBUCKET0 + b, str); } TMSP_constraint* last_constraint = NULL; const long totart = _articles.sort(); TWait_cursor hourglass; for (long i = 0; i < totart; i++) { TMSP_line& line = _articles[i]; TMSP_constraint* curr_constraint = find_constraint(line,TRUE); if (last_constraint != curr_constraint) { // aggiunge la linea di vincolo TToken_string& consrow = sf.row(-1); curr_constraint->fill_sheet_row(consrow, *this); consrow.add("** Ordini ",F_DESCART-FIRST_FIELD); sf.disable_cell(sf.items()-1, -1); // aggiunge la linea della giacenza prog TToken_string& rowgiac = sf.row(-1); copy_sheet_row(rowgiac,consrow); rowgiac.add("** Giacenza teorica",F_DESCART-FIRST_FIELD); sf.disable_cell(sf.items()-1, -1); last_constraint = curr_constraint; curr_constraint->set_on_sheet(); } TToken_string& row = sf.row(-1); line.fill_sheet_row(row, *this); } const long totcon = _constraints.sort(); for (long j = 0; j < totcon; j++) { TMSP_constraint& cons = _constraints[j]; if (!cons.is_on_sheet()) { // aggiunge le tre linee TToken_string& row = sf.row(-1); cons.fill_sheet_row(row, *this); cons.set_on_sheet(); sf.disable_cell(sf.items()-1, -1); //linea della giacenza prog TToken_string& rowgiac = sf.row(-1); copy_sheet_row(rowgiac,row); sf.disable_cell(sf.items()-1, -1); //linea articolo (vuota) TMSP_line line(cons); line.fill_sheet_row(sf.row(-1), *this); rowgiac.add("** Giacenza teorica",F_DESCART-FIRST_FIELD); row.add("** Ordini ",F_DESCART-FIRST_FIELD); } } for (int r = sf.items()-1; r >0 ; ) { // init bucket 0 TCodice_articolo codart(sf.cell(r,sf.cid2index(F_ARTICOLO))); if (codart.blank()) r = r-2; TArticolo_giacenza art(codart); r=init_bucket0(art, r); } sf.force_update(); } bool TMSP_mask::distinta_master(const char *code) { const TRectype &rec=cache().get(LF_DIST,code); if (rec.empty()) return FALSE; return rec.get_bool("MASTER"); } int TMSP_mask::find_distinta_master(const char * code, TString & master,TString & livmaster,TString & um, real & expr, TString & imp,TString & lin,TString & magdep, int fromindex) { TArray sons; if (_dist_tree.set_root(code)) { _dist_tree.explode(sons, FALSE, RAGGR_EXP_NONE, 1); TRiga_esplosione* riga; while (riga=(TRiga_esplosione* )sons.objptr(fromindex)) { if (distinta_master(riga->articolo())) { master=riga->articolo(); livmaster=riga->livello(); um =riga->um(); expr =riga->val(); return fromindex; } fromindex=sons.succ(fromindex); } } master=""; return -1; } bool TMSP_mask::insert_propose(bool verbose) { if (_proposed_articles.items()<=0L) { if (verbose) message_box("Nessuna nuova proposta"); return FALSE; } else { add_or_sub_propose(+1); const long new_rows=_proposed_articles.items(); TSheet_field& sf = sfield(F_ARTICOLI); int firstbuck; TDate firstdate(get_date(F_ADATA)); for (long new_row=0; new_row < new_rows; new_row++) { const TDate & d=_proposed_articles[new_row].time(0).date() ; firstdate=min(firstdate,d); } firstbuck=int((firstdate- get_date(F_DADATA))/days_per_bucket())+1; if (verbose) message_box("Quantita' proposte a partire dal %s (colonna %0d)", (const char *)firstdate.string(), int(firstbuck)); } return TRUE; } bool TMSP_mask::remove_propose(bool verbose) { if (_proposed_articles.items()<=0L) { if (verbose) message_box("Nessuna proposta da annullare"); return FALSE; } add_or_sub_propose(-1); _proposed_articles.destroy(); return TRUE; } void TMSP_mask::add_or_sub_propose(char sign) { const long new_rows=_proposed_articles.items(); TSheet_field& sf = sfield(F_ARTICOLI); for (long new_row=0; new_row < new_rows; new_row++) { const TMRP_line & a=_proposed_articles[new_row]; TMSP_line line(a.codcli(), a.codice(), a.livgiac(), a.codimp(), a.codlin(), a.codmag()); bool line_found=FALSE; TMSP_constraint* constraint= find_constraint(line,FALSE); if (constraint==NULL) { CHECK( sign>0, "Impossibile trovare il vincolo da soddisfare..."); // aggiunge le tre linee constraint=new TMSP_constraint(line); TToken_string& row = sf.row(-1); constraint->fill_sheet_row(row, *this); constraint->set_on_sheet(); sf.disable_cell(sf.items()-1, -1); //linea della giacenza prog TToken_string& rowgiac = sf.row(-1); copy_sheet_row(rowgiac,row); sf.disable_cell(sf.items()-1, -1); //linea articolo (vuota) TToken_string& emptyrowc = sf.row(-1); rowgiac.add("** Giacenza teorica",F_DESCART-FIRST_FIELD); row.add("** Ordini ",F_DESCART-FIRST_FIELD); } int art_row; for (art_row=int(sf.items())-1;art_row >=0; art_row--) { TToken_string &row=sf.row(art_row); long codcli; TCodice_articolo codart ; TString16 liv, mag, codimp, codlin, codum; msprow2codes(row,codcli,codart,liv, mag, codimp, codlin, codum); if (!sf.cell_disabled(art_row, F_BUCKET1-FIRST_FIELD)) { line_found=(a.codcli()==codcli && a.codice()==codart && a.livgiac()==liv && a.codimp()==codimp && a.codlin()==codlin && a.codmag()==mag ); if (line_found) break; } else { TMSP_constraint* currcons=_constraints.find(codcli, codart, liv, codimp, codlin, mag); if (constraint==currcons) break; } } if (!line_found) { art_row++; sf.insert(art_row); TToken_string& artrow = sf.row(art_row); constraint->fill_sheet_row(artrow, *this,TRUE); artrow.add(a.codimp(), F_CODIMP-FIRST_FIELD); artrow.add(a.codlin(), F_CODLIN-FIRST_FIELD); TString16 str=a.codmag().left(3); artrow.add(str, F_MAGAZZINO-FIRST_FIELD); str=a.codmag().mid(3); artrow.add(str, F_DEPOSITO-FIRST_FIELD); } TToken_string& artrow = sf.row(art_row); const int lastbuck =_proposed_articles[new_row].last_bucket(); //TDate data_buck; for (int bucket=0; bucket<=lastbuck ; bucket++) { int b= int((_proposed_articles[new_row].time(bucket).date() - get_date(F_DADATA))/days_per_bucket()); real art_per_buck(artrow.get(F_BUCKET1 + b*2 - FIRST_FIELD)); if (sign>0) art_per_buck+=_proposed_articles[new_row].sched_receipts(bucket); else { if (art_per_buck >= _proposed_articles[new_row].sched_receipts(bucket)) art_per_buck-=_proposed_articles[new_row].sched_receipts(bucket); else art_per_buck=ZERO; } artrow.add(art_per_buck.string(), F_BUCKET1 + b*2 - FIRST_FIELD); real price; if (!art_per_buck.is_zero()) find_price(get(F_TIPOCV),get(F_CODCONDV),get(F_CATVEN_CV), get(F_TIPOCF), a.codcli(), a.codice(), art_per_buck, price); artrow.add(price.string(), F_BUCKET1 + b*2 + 1 - FIRST_FIELD); } test_art_row(art_row,FALSE); } sf.force_update(); } // compatta le linee togliendo i doppioni void TMSP_mask::pack_article_sheet() { TSheet_field& sf = sfield(F_ARTICOLI); TToken_string lastrow; for (int art_row=int(sf.items())-1; art_row >=0; art_row--) { if (!sf.cell_disabled(art_row, F_BUCKET1-FIRST_FIELD)) { TToken_string &row=sf.row(art_row); long codcli = atol(row.get(F_CLIENTE-FIRST_FIELD)); TCodice_articolo codart = row.get(F_ARTICOLO-FIRST_FIELD); TString16 codimp= row.get(F_CODIMP-FIRST_FIELD); TString16 codlin= row.get(F_CODLIN-FIRST_FIELD); TString16 liv; _livelli.pack_grpcode(liv, row.get(sf.cid2index(F_LIV1)),1); _livelli.pack_grpcode(liv, row.get(sf.cid2index(F_LIV2)),2); _livelli.pack_grpcode(liv, row.get(sf.cid2index(F_LIV3)),3); _livelli.pack_grpcode(liv, row.get(sf.cid2index(F_LIV4)),4); TString16 mag; add_magcode(mag, row.get(F_MAGAZZINO-FIRST_FIELD)); add_depcode(mag, row.get(F_DEPOSITO-FIRST_FIELD)); for (int bucket=0; bucket<=LAST_BUCKET ; bucket++) { real art_per_buck(lastrow.get(F_BUCKET1 + bucket*2 - FIRST_FIELD)); row.add(art_per_buck.string(), F_BUCKET1 + bucket*2 - FIRST_FIELD); } } } sf.force_update(); } void TMSP_mask::check_articles() { TSheet_field& sf = sfield(F_ARTICOLI); int items=sf.items(); if (items==0) { message_box("Nessun articolo da verificare"); return; } TMSPCheck_mask cm(this); cm.run(); cm.gopos_mainmask(); } void TMSP_mask::check_capacities() { TSheet_field& sf = sfield(F_LINEE); int items=sf.items(); if (items==0) { message_box("Nessun carico di linea da verificare"); return; } TCRPCheck_mask cm(this); cm.run(); set_focus_field(sf.dlg()); sf.set_focus_cell(cm.last_row(),cm.last_col()+F_LBUCKET0); } bool TMSP_mask::sort_sheet() { TWait_cursor hg; TSheet_field& a = sfield(F_ARTICOLI); bool ok=TRUE; switch (-get_int(F_MSP_SORT)) { case SORT_BY_CAL: a.sort(TRiga_articolo::order_compareCAL); break; case SORT_BY_CLA: a.sort(TRiga_articolo::order_compareCLA); break; case SORT_BY_ACL: a.sort(TRiga_articolo::order_compareACL); break; case SORT_BY_ALC: a.sort(TRiga_articolo::order_compareALC); break; case SORT_BY_LCA: a.sort(TRiga_articolo::order_compareLCA); break; case SORT_BY_LAC: a.sort(TRiga_articolo::order_compareLAC); break; default: return FALSE; } a.force_update(); return ok; } void TMSP_mask::add_MRP_bucket(TMRP_line& new_article, int nbucket, const real &curr_arts) { TDate data_buck; data_buck=get_date(F_DADATA); data_buck+=days_per_bucket()*(nbucket-1); TMRP_time t(data_buck, 0, "", ""); new_article.add_sched_rec(t, curr_arts ); } void TMSP_mask::propose_1stJIT(TMSP_logic logic, bool check_machine, bool check_human) { const bool useextralines=get_bool(F_USENONSTDLIN_MSP); const bool useextrahours=get_bool(F_USEEXTRAHRS_MSP) ; const bool lotsizing=get_bool(F_LOTSIZING) ; const bool no_outcapacity=get_bool(F_OUTOFCAPACITY) ; const int anticipomin=get_int(F_ANTICIPOMIN); const int anticipomax=get_int(F_ANTICIPOMAX); bool ok=TRUE; // per sicurezza devo (ri-)calcolare il carico attuale..... _capacities.destroy(); if (check_machine || check_human) { if (ok = load_MRP_lines(_actual_schedule)) { nettify_MRP_lines(TRUE,FALSE); ok = general_review(check_machine, check_human, useextralines, useextrahours,FALSE, no_outcapacity,0, 0,_actual_schedule, logic); } } // (two) levels master scheduling TMRP_lines _1stlevel; bool some=FALSE; for (int level=1; ok && level <= 2; level++) { if (ok = load_MRP_lines(_stock_break,level)) { _proposed_articles.destroy(); nettify_MRP_lines(FALSE, lotsizing); if (ok = general_review(check_machine, check_human, useextralines, useextrahours,lotsizing, no_outcapacity,anticipomin,anticipomax,_stock_break, logic)) some = insert_propose(some || level==2); if (level==1) _1stlevel=_proposed_articles; if (level==2) _proposed_articles.add(_1stlevel); } } if (!ok) remove_propose(); _capacities.sort(); } /* void TMSP_mask::propose_firstfit(bool check_machine, bool check_human) { const bool useextralines=get_bool(F_USENONSTDLIN_MSP); const bool useextrahours=get_bool(F_USEEXTRAHRS_MSP) ; const bool lotsizing=get_bool(F_LOTSIZING) ; const bool no_outcapacity=get_bool(F_OUTOFCAPACITY) ; const int anticipomin=get_int(F_ANTICIPOMIN); const int anticipomax=get_int(F_ANTICIPOMAX); bool ok=TRUE; // per sicurezza devo (ri-)calcolare il carico attuale..... _capacities.destroy(); if (check_machine || check_human) { if (ok = load_MRP_lines(_actual_schedule)) { nettify_MRP_lines(TRUE,FALSE); ok = firstfit_review(check_machine, check_human, useextralines, useextrahours,FALSE, no_outcapacity,0, 0,_actual_schedule); } } // (two) levels master scheduling TMrp_lines _1stlevel; for (int level=1; level <= 2; level++) { if (ok && (ok = load_MRP_lines(_stock_break,level))) { nettify_MRP_lines(FALSE, lotsizing); if (ok = firstfit_review(check_machine, check_human, useextralines, useextrahours,lotsizing, no_outcapacity,anticipomin,anticipomax,_stock_break)) { insert_propose(); _1stlevel=_proposed_articles(); } } } if (!ok) remove_propose(); _capacities.sort(); } // propone i valori con logica Just-in-time void TMSP_mask::propose_JIT(bool check_machine, bool check_human) { const bool useextralines=get_bool(F_USENONSTDLIN_MSP); const bool useextrahours=get_bool(F_USEEXTRAHRS_MSP) ; const bool lotsizing=get_bool(F_LOTSIZING) ; const bool no_outcapacity=get_bool(F_OUTOFCAPACITY) ; const int anticipomin=get_int(F_ANTICIPOMIN); const int anticipomax=get_int(F_ANTICIPOMAX); bool ok=TRUE; // per sicurezza devo (ri-)calcolare il carico attuale..... _capacities.destroy(); if (check_machine || check_human) { if (ok = load_MRP_lines(_actual_schedule)) { nettify_MRP_lines(TRUE, FALSE); ok = JIT_review(check_machine, check_human, useextralines, useextrahours,FALSE, no_outcapacity,0, 0,_actual_schedule); } } // (two) levels master scheduling for (int level=1; level < 2; level++) { if (ok && (ok = load_MRP_lines(_stock_break, 1))) { nettify_MRP_lines(FALSE, lotsizing); if (ok = JIT_review(check_machine, check_human, useextralines, useextrahours,FALSE, no_outcapacity,anticipomin,anticipomax,_stock_break)) insert_propose(); } } if (!ok) remove_propose(); _capacities.sort(); } bool TMSP_mask::firstfit_review(bool check_machine, bool check_human, bool useextralines, bool useextrahours,bool lotsizing, bool no_outcapacity, int anticipomin,int anticipomax, TMSP_mode mode) { return general_review(check_machine, check_human, useextralines, useextrahours,lotsizing, no_outcapacity, anticipomin,anticipomax, mode, _first_fit_logic); } bool TMSP_mask::JIT_review(bool check_machine, bool check_human, bool useextralines, bool useextrahours,bool lotsizing, bool no_outcapacity, int anticipomin,int anticipomax, TMSP_mode mode) { return general_review(check_machine, check_human, useextralines, useextrahours,lotsizing, no_outcapacity, anticipomin,anticipomax, mode, _JIT_logic); } */ // propone i valori con logica first-fit // cerca il primo bucket con capacitą produttiva bool TMSP_mask::general_review(bool check_machine, bool check_human, bool useextralines, bool useextrahours,bool lotsizing, bool no_outcapacity, int anticipomin,int anticipomax, TMSP_mode mode, TMSP_logic logic ) { const bool ignore_mag=get_bool(F_NOMAG_IN); const bool ignore_imp=get_bool(F_NOIMP_IN); const bool ignore_lin=get_bool(F_NOLIN_IN); TLavorazione *curr_labor; TLinea_prod linea_prod; TArray labors; const long max_mrp_rows=_mrp_articles.items(); const char *msg; if (mode == _actual_schedule) msg="Calcolo carico attuale"; else msg="Calcolo nuove proposte"; TProgind pi(max_mrp_rows,msg, TRUE, TRUE); for (long mrp_row=0; mrp_row < max_mrp_rows; mrp_row++) { pi.addstatus(1); if (pi.iscancelled()) return FALSE; TMRP_line &mrpline=_mrp_articles[mrp_row]; TString16 livello_di_giacenza(mrpline.livgiac()); TString16 codlin(mrpline.codlin()); TString16 codimp(mrpline.codimp()); bool no_labor=!_dist_tree.set_root(mrpline.codice()); if (!no_labor) { TDate data_buck; // la logica del ciclo sulle lav e' da invertire: prima il bucket , poi le lavorazioni // se si vogliono caricare pił lavorazioni TRiga_esplosione * l=_dist_tree.first_critical_labor(labors); no_labor=(l==NULL); if (!no_labor) { curr_labor=TDistinta_tree::find_labor(l); // cerca le linee sulle quali e' possibile eseguire questa lavorazione // e la carica con la lavorazione relativa all'articolo corrente const int lineestd = curr_labor->linee_standard(); const int maxlinee = useextralines ? curr_labor->linee() : lineestd; TCRP_line *crpline,*crpline_imp,*crpline_firm; const int lastbuck =mrpline.last_bucket(); for (int bucket=0; bucket<=lastbuck ; bucket++) { real curr_arts, art_per_buck(mrpline.net_requirement(bucket)); // quantitą da produrre nel bucket real load, capacity, unit_load, hload, hcapacity, unit_hload ; // carichi di linea int nbucket, lower_buck, upper_buck; int attemp=1; // primo tentativo: rimane entro i vincoli di minimo e massimo anticipo utilizando solo le linee STD // secondo tentativo: rimane entro i vincoli di minimo e massimo anticipo utilizando anche le linee non STD // terzo tentativo: supera i vincoli di minimo e massimo anticipo utilizando anche le linee non STD art_per_buck.round(5); while (art_per_buck > ZERO && attemp <= 3) { if (logic == _first_fit_logic) { lower_buck = bucket - anticipomax/days_per_bucket(); upper_buck = (attemp <= 2 ? bucket-anticipomin/days_per_bucket(): LAST_BUCKET); nbucket=lower_buck; } else { lower_buck = (attemp <= 2 ? bucket-anticipomax/days_per_bucket(): 0); upper_buck = bucket - anticipomin/days_per_bucket(); nbucket=upper_buck; } if (lower_buck<1) lower_buck=1; if (nbucket<1) nbucket=1; do { int nlinea, numlinee; if (ignore_lin || codlin.blank() ) { nlinea=(attemp == 2 ? lineestd : 0 ) ; numlinee=(attemp == 1 ? lineestd : maxlinee ) ; } else { nlinea= curr_labor->find_linea(codlin); if (nlinea<0) { error_box("La linea %s non e' in grado di produrre l'articolo %s", (const char * )codlin, (const char *) _mrp_articles[mrp_row].codice()); break; } numlinee=nlinea+1; } while (art_per_buck > ZERO && nlinea >= 0 && nlineacod_linea(nlinea)); if ( ignore_imp || codimp.blank() || codimp == linea_prod.codimp()) { crpline=_capacities.find(linea_prod); if (crpline==NULL) crpline=compute_capacity(linea_prod,useextralines, useextrahours); crpline_imp =_capacities.find(linea_prod.codimp()); crpline_firm=_capacities.find(); int human_level=1; if ( linea_prod.personale_dedicato()) human_level=3; else if (linea_prod.get_impianto()->personale_dedicato()) human_level=2; // individua il massimo caricabile sulla linea unit_load = l->val()*curr_labor->um_temporale().converti_in_ore()/curr_labor->produttiv_linea(nlinea); unit_hload=unit_load*curr_labor->numpers_linea(nlinea); capacity = check_machine ? crpline->capacity(nbucket) : MAXCAPACITY; load = crpline->load(nbucket); switch (human_level) { case 3: hcapacity = crpline->human_capacity(nbucket) ; hload = crpline->human_load(nbucket); break; case 2: hcapacity = crpline_imp->human_capacity(nbucket) ; hload = crpline_imp->human_load(nbucket); break; default: hcapacity = crpline_firm->human_capacity(nbucket) ; hload = crpline_firm->human_load(nbucket); break; } // ridimensiona al massimo numero di articoli dovuto alle macchine curr_arts = art_per_buck ; if (mode != _actual_schedule /*|| no_outcapacity*/) { if (load + curr_arts * unit_load > capacity ) { curr_arts = (capacity - load ) / unit_load; curr_arts.floor(); } // ridimensiona al massimo numero di articoli dovuto alle macchine if (check_human && hload + curr_arts * unit_hload > hcapacity) { curr_arts = (hcapacity - hload) / unit_hload; curr_arts.floor(); } /* // elimina i picchi di capacita' (errato) if (curr_arts != art_per_buck && mode == _actual_schedule) { TMRP_line* new_article = _proposed_articles.find(mrpline.codice(), mrpline.livgiac(), mrpline.codmag(), linea_prod.codimp() , linea_prod.codice(), mrpline.codcli(), TRUE); add_MRP_bucket(*new_article, nbucket, curr_arts-art_per_buck ); }*/ } if (curr_arts>ZERO) { // aggiunge il carico macchina art_per_buck-=curr_arts; load= curr_arts * unit_load; crpline->add_load(nbucket, load ); // aggiunge il carico uomo load*=curr_labor->numpers_linea(nlinea); switch (human_level) { case 3: crpline->add_human_load(nbucket, load ); case 2: crpline_imp->add_human_load(nbucket, load ); default: crpline_firm->add_human_load(nbucket, load ); } if (mode == _stock_break) { TMRP_line* new_article = find_propose(mrpline.codcli(), mrpline.codice(), mrpline.livgiac(), linea_prod.codimp() , linea_prod.codice(), mrpline.codmag(), TRUE); add_MRP_bucket(*new_article, nbucket, curr_arts); } } } else error_box("Articolo %s: impianto %s incompatibile con la linea %s",(const char *)mrpline.codice(), (const char *)codimp, (const char *)linea_prod.codice()); nlinea++; } // ciclo sulle linee if (logic == _first_fit_logic) nbucket++; else nbucket--; } while(art_per_buck > ZERO && nbucket>=lower_buck && nbucket <= upper_buck ); attemp++; // altro tentativo } } l=_dist_tree.next_critical_labor(labors); } } if (no_labor) // nessuna lavorazione da controllare o capacita' infinita { if (mode == _stock_break) { const int lastbuck =mrpline.last_bucket(); for (int b=0; b<=lastbuck ; b++) { real art_per_buck(mrpline. net_requirement(b));// quantitą da produrre nel bucket if (art_per_buck > ZERO) { int nbucket; if (logic == _first_fit_logic) nbucket = b - anticipomax/days_per_bucket(); else nbucket = b - anticipomin/days_per_bucket(); if (nbucket<1) nbucket=1; TMRP_line* new_article = find_propose(mrpline.codcli(), mrpline.codice(), mrpline.livgiac(), mrpline.codimp() , mrpline.codlin(), mrpline.codmag(), TRUE); add_MRP_bucket(*new_article, nbucket, art_per_buck); } } } } } return TRUE; } bool TMSP_mask::capacity_review(bool useextralines, bool useextrahours) { bool ok; if (!load_MRP_lines(_actual_schedule)) return FALSE; nettify_MRP_lines(TRUE); _capacities.destroy(); if (get(F_RECALC_TYPE).left(1)=="1") ok = general_review(TRUE, TRUE, useextralines, useextrahours, FALSE, FALSE, 0, 0, _actual_schedule,_first_fit_logic); else if (get(F_RECALC_TYPE).left(3)=="JIT") ok = general_review(TRUE, TRUE, useextralines, useextrahours, FALSE, FALSE, 0, 0, _actual_schedule,_first_fit_logic); _capacities.sort(); return ok; } TCRP_line *TMSP_mask::compute_capacity(TLinea_prod &lineaprod, bool useextralines, bool useextrahours) { TMRP_calendar & cal = TMRP_time::get_calendar("", lineaprod.codice()); TMRP_calendar & cal_imp = TMRP_time::get_calendar("", lineaprod.codimp()); TMRP_calendar & cal_firm = TMRP_time::get_calendar(""); CHECK(_capacities.find(lineaprod,FALSE)==NULL,"Errore: impossibile ricalcolare la capacita' di una linea produttiva"); TCRP_line *crpline,*crpline_imp,*crpline_firm; crpline =_capacities.find(lineaprod, "", 0L,TRUE); const bool compute_imp = (_capacities.find(lineaprod.codimp(), "", "", 0L)==NULL); crpline_imp =_capacities.find(lineaprod.codimp(), "", "", 0L, TRUE); const bool compute_firm = (_capacities.find("", "", "", 0L)==NULL); crpline_firm=_capacities.find("", "", "", 0L, TRUE); // calcola la capacita' TDate data_buck=get_date(F_DADATA); real capacity,human,human_imp,human_firm; for (int b=1; b=0 ; day--) { cal.add_oremacchina(capacity, data_buck,useextrahours); if (lineaprod.personale_dedicato()) cal.add_oreuomo(human, data_buck,useextrahours); if (compute_imp && lineaprod.get_impianto()->personale_dedicato()) cal_imp.add_oreuomo(human_imp, data_buck,useextrahours); if (compute_firm) cal_firm.add_oreuomo(human_firm, data_buck,useextrahours); ++data_buck; } crpline->set_capacity(b,capacity); if (human > ZERO) { crpline->set_human_capacity(b,human); crpline_imp->set_human_capacity(b,human+crpline_imp->human_capacity(b)); crpline_firm->set_human_capacity(b,human+crpline_firm->human_capacity(b)); } else crpline->set_human_capacity(b,ZERO); if (human_imp > ZERO) { crpline_imp->set_human_capacity(b,human_imp+crpline_imp->human_capacity(b)); crpline_firm->set_human_capacity(b,human_imp+crpline_firm->human_capacity(b)); } else crpline_imp->set_human_capacity(b,ZERO); if (human_firm > ZERO) crpline_firm->set_human_capacity(b,human_firm+crpline_firm->human_capacity(b)); } return crpline; } TMRP_line* TMSP_mask::find_propose( long codcli, const char * codart,const char * liv, const char * codimp,const char * codlin, const char * mag, bool Force) { return _proposed_articles.find(codart,liv,mag,codimp,codlin,codcli, Force); } // carica le MRP line dallo sheet articoli void TMSP_mask::msprow2codes(TToken_string &row,long &clifor, TCodice_articolo &art , TString & liv, TString & mag, TString & imp, TString & lin, TString & um) { clifor = row.get_long(F_CLIENTE-FIRST_FIELD); art = row.get(F_ARTICOLO-FIRST_FIELD); _livelli.pack_grpcode(liv, row.get(F_LIV1-FIRST_FIELD),1); _livelli.pack_grpcode(liv, row.get(F_LIV2-FIRST_FIELD),2); _livelli.pack_grpcode(liv, row.get(F_LIV3-FIRST_FIELD),3); _livelli.pack_grpcode(liv, row.get(F_LIV4-FIRST_FIELD),4); add_magcode(mag,row.get(F_MAGAZZINO-FIRST_FIELD)); add_depcode(mag,row.get(F_DEPOSITO-FIRST_FIELD)); imp = row.get(F_CODIMP-FIRST_FIELD); imp.trim(); lin = row.get(F_CODLIN-FIRST_FIELD); lin.trim(); um = row.get(F_UM-FIRST_FIELD); um.trim(); } void TMSP_mask::crprow2codes(TToken_string &row, TString & imp, TString & lin, long &clifor, TCodice_articolo &art , TString & liv, TString & um) { clifor=0L;//clifor = row.get_long(F_CLIENTE-FIRST_FIELD); //art = row.get(F_ARTICOLO-FIRST_FIELD); //_livelli.pack_grpcode(liv, row.get(F_LIV1-FIRST_FIELD),1); //_livelli.pack_grpcode(liv, row.get(F_LIV2-FIRST_FIELD),2); //_livelli.pack_grpcode(liv, row.get(F_LIV3-FIRST_FIELD),3); //_livelli.pack_grpcode(liv, row.get(F_LIV4-FIRST_FIELD),4); imp = row.get(F_CODIMPCRP-FIRST_FIELD); imp.trim(); lin = row.get(F_CODLINCRP-FIRST_FIELD); lin.trim(); um = row.get(F_LUM-FIRST_FIELD); um.trim(); } // carica le MRP line dallo sheet articoli bool TMSP_mask::load_MRP_lines(TMSP_mode mode, int level) { TSheet_field& sa = sfield(F_ARTICOLI); if (sa.items()==0) { message_box("Nessun articolo da valutare"); return FALSE; } const int a_buck0 = sa.cid2index(F_BUCKET0); TDate data_buck; _mrp_articles.destroy(); // calcola gli impegni di produzione per gli articoli dello sheet bool skip=FALSE; TMRP_line* last_constraint = NULL; FOR_EACH_SHEET_ROW(sa, r, row) if (!sa.cell_disabled(r, a_buck0+2) || sa.cell_disabled(r+1, a_buck0+2)) { const bool is_constraint=sa.cell_disabled(r, a_buck0+2); if (is_constraint) { const bool has_mastercodes=(*row->get(F_MASTERCODE-FIRST_FIELD)=='M'); skip = (level == 1 && !has_mastercodes || level == 2 && has_mastercodes ); } if (!skip) { long clifor; TCodice_articolo art ; TString16 liv,mag, imp, lin; TCodice_um um ; msprow2codes(*row,clifor, art , liv, mag, imp, lin, um); TMRP_line* curr_article = _mrp_articles.find(art, liv, mag, imp, lin, clifor ,TRUE); if (is_constraint) last_constraint=curr_article; bool added_some=FALSE; for (int nbucket=LAST_BUCKET-1; nbucket>=0 ; nbucket--) { data_buck=get_date(F_DADATA); data_buck+=days_per_bucket()*nbucket; const TMRP_time t(data_buck, 0, imp, lin); real qta=row->get(a_buck0+nbucket*2); if (qta != ZERO) { TQuantita q(art, um, qta); q.convert2umbase(); // mette i vincoli nel gross req // e le celle normali negli sched receipts switch (mode) { case _actual_schedule: if (!is_constraint) { curr_article->add_gross_req(t, q.val()); curr_article->add_sched_rec(t, q.val()); } break; case _stock_break: if (is_constraint) { curr_article->add_gross_req(t, q.val()); last_constraint=curr_article; } else { if (last_constraint) last_constraint->add_sched_rec(t, q.val()); } break; } added_some=TRUE; } else if (added_some) curr_article->set_on_hand(t, ZERO); if (nbucket == 0) curr_article->set_on_hand(t, curr_article->giacenza_attuale(qta,data_buck)); } } } return TRUE; } // is_net_requirement: dice se le quantitą presenti sono da considerare gia' come net req o // se devo prenderle come scheduled rec e il net req lo devo calcolare di conseguenza bool TMSP_mask::nettify_MRP_lines(bool already_net, bool lotsizing) { // *********** // gross 2 net const long max_mrp_rows=_mrp_articles.items(); for (long mrp_row=0; mrp_row < max_mrp_rows; mrp_row++) { // calcola il fabbisogno netto e lo riporta sul bucket seguente TMRP_line & mrp_line = _mrp_articles[mrp_row]; const int last = mrp_line.last_bucket(); for (int bucket = 0; bucket <= last; bucket = mrp_line.next_bucket(bucket)) { real netreq,giacres; if (already_net) { // generazione del master schedule plan; le quantitą sono da considerare nette netreq= mrp_line.sched_receipts(bucket); giacres=0; mrp_line.set_net_req(bucket, netreq ); } else { // generazione delle pianificazioni (commesse, Master Production Plan (non per famiglie), prove di budget ecc) giacres = mrp_line.on_hand(bucket); giacres += mrp_line.sched_receipts(bucket); giacres -= mrp_line.gross_requirement(bucket); if (lotsizing) netreq = mrp_line.sizeup_net_requirement(bucket, giacres); else { netreq = -giacres; netreq = mrp_line.set_net_req(bucket, netreq); } giacres += netreq; } if (giacres > ZERO) { const int nb = mrp_line.next_bucket(bucket); if (nb <= mrp_line.last_bucket()) mrp_line.set_on_hand(nb, giacres); } } } return max_mrp_rows>0; } int TMSP_mask::days_per_bucket() const { return get_int(F_DAYXBUCK) * (get_int(F_BUCKETS) ? get_int(F_BUCKETS) : get_int(F_BUCKET)); } void TMSP_mask::clear_sheets() { TSheet_field &sa=sfield(F_ARTICOLI); if (sa.items()>0) { sa.destroy(); sa.force_update(); } TSheet_field &sl=sfield(F_LINEE); if (sl.items()>0) { sl.destroy(); sl.force_update(); } // sfield(F_TESTE).destroy(); // sfield(F_TESTE).force_update(); xvt_statbar_set(""); } // ********** // imposta il bucket 0 ; se i flag sulla maschera sono settati, // - sulla riga delle giacenze progressive : somma la giacenza iniziale // - sulla riga dell'ordinato progressivo => sottrae la giacenza iniziale // ********** // valore di ritorno:restituisce l'ultima linea dello sheet processata int TMSP_mask::init_bucket0(TArticolo_giacenza &art, int r) { TSheet_field& sf = sfield(F_ARTICOLI); const bool add_giac=get_bool(F_MSCHEDULEPLAN);// nettifica la giacenza // Calcola l'indice c della riga con la giacenza prog const int b0 = sf.cid2index(F_BUCKET0); const int b1 = sf.cid2index(F_BUCKET1); real planned; for (int c = r; c > 1; c--) { if (sf.cell_disabled(c, b1)) break; planned += real(sf.cell(c, b0)); } TToken_string &giac_row=sf.row(c); TToken_string &ord_row=sf.row(c-1); // calcola codice di magazzino e di livello TString16 livello; _livelli.pack_grpcode(livello, giac_row.get(sf.cid2index(F_LIV1)),1); _livelli.pack_grpcode(livello, giac_row.get(sf.cid2index(F_LIV2)),2); _livelli.pack_grpcode(livello, giac_row.get(sf.cid2index(F_LIV3)),3); _livelli.pack_grpcode(livello, giac_row.get(sf.cid2index(F_LIV4)),4); TString16 codmag; add_magcode(codmag, giac_row.get(sf.cid2index(F_MAGAZZINO))); add_depcode(codmag, giac_row.get(sf.cid2index(F_DEPOSITO))); // calcola i bucket 0 TQuantita prog_giac(art.codice(),giac_row.get(sf.cid2index(F_UM)),planned); real giac=art.giacenza_anno(codmag, livello, get_date(F_DADATA).year()); if (add_giac) prog_giac +=giac; prog_giac += giac_row.get(b0); giac_row.add(prog_giac.val().string(), b0); do_test_art_row(r,c,FALSE); return c-2; } bool TMSP_mask::do_test_art_row(int r, int c, bool signal) { TSheet_field& sf = sfield(F_ARTICOLI); TMask & smask= sf.sheet_mask(); TToken_string & curr_row=sf.row(r); TToken_string & constr_row=sf.row(c-1); TToken_string & giac_row=sf.row(c); TCodice_articolo codart(curr_row.get(sf.cid2index(F_ARTICOLO))); const int b0 = sf.cid2index(F_BUCKET0); // calcola la giacenza proiettata TQuantita vincolo(codart,giac_row.get(sf.cid2index(F_UM)),ZERO); TQuantita totale(vincolo),qta(vincolo); totale += real(sf.cell(c, b0));// giacenza iniziale for (int b = 1; b < LAST_BUCKET; b++) { vincolo.set_val(real(sf.cell(c-1, b0+b*2))); for (int i = c+1; i <= r; i++) { totale += real(sf.cell(i, b0+b*2)); // somma alla giacenza le produzioni } totale -= real(sf.cell(c-1, b0+b*2)); // detrae le uscite giac_row.add(totale.val().string(), b0+b*2); if (signal && totale.val() < 0) { TString err; const int bucket_size = days_per_bucket(); const TDate d = get_date(F_DADATA) + long(bucket_size * (b-1)); err << "Riga " << c+1 << ": Vincolo non ripettato al " << d; beep(); signal =FALSE; xvt_statbar_set(err); } } sf.force_update(c); if (signal) xvt_statbar_set(""); if (constr_row.get_char(sf.cid2index(F_MASTERCODE))=='M') { // aggancia i master di 2' livello TString16 liv; _livelli.pack_grpcode(liv, curr_row.get(sf.cid2index(F_LIV1)),1); _livelli.pack_grpcode(liv, curr_row.get(sf.cid2index(F_LIV2)),2); _livelli.pack_grpcode(liv, curr_row.get(sf.cid2index(F_LIV3)),3); _livelli.pack_grpcode(liv, curr_row.get(sf.cid2index(F_LIV4)),4); TString16 imp(curr_row.get(sf.cid2index(F_CODIMP)));//imp.trim(); TString16 lin(curr_row.get(sf.cid2index(F_CODLIN)));//lin.trim(); TString16 mag; add_magcode(mag, curr_row.get(sf.cid2index(F_MAGAZZINO))); add_depcode(mag, curr_row.get(sf.cid2index(F_DEPOSITO))); TMSP_line mspline(curr_row.get_long(F_CLIENTE-FIRST_FIELD), codart, liv, imp, lin, mag); TMSP_constraint*currconstr=find_constraint(mspline); CHECK (currconstr, "Impossibile trovare il vincolo dell'articolo di 1' livello"); int i=0; TMaster_code *mc; while ((mc=currconstr->get_mastercode(i++))!=NULL) { TMSP_constraint* line_master=_constraints.find(currconstr->cliente(),mc->articolo() , mc->livello(), mc->codimp(), mc->codlin(), mc->codmag()); CHECK (line_master, "Impossibile trovare il vincolo dell'articolo di 2' livello"); CHECK (line_master->has_upperlines(), "La linea master non risulta avere dipendenze di alcun tipo"); int mrow= find_constr_row(*line_master); real q2make(giac_row.get(sf.cid2index(F_BUCKET0))); for (int b = 1; b <= LAST_BUCKET; b++) { const int b2=b-int(long(mc->leadtime()/days_per_bucket()+0.5)); if (b2>=0) { q2make=real(giac_row.get(sf.cid2index(F_BUCKET0+b*2)))+ real(constr_row.get(sf.cid2index(F_BUCKET0+b*2)))- real(giac_row.get(sf.cid2index(F_BUCKET0+b*2-2))); if (q2make > ZERO) q2make = (q2make)*mc->expr(); else q2make = ZERO; TMSP_constraint* upperline=line_master->get_upperline(*currconstr); real oldq2make=upperline->qta(b2); real totale2make=line_master->qta(b2); line_master->qta(b2) = line_master->qta(b2) - upperline->qta(b2) + q2make; upperline->qta(b2) = q2make; TToken_string &master_row=sf.row(mrow); master_row.add(line_master->qta(b2).string(),sf.cid2index(F_BUCKET0+b2*2)); } } sf.force_update(mrow); int first,last; TRiga_articolo::find_block(sf, mrow+2, first, last); do_test_art_row(last, first, signal); } } return c-2; } bool TMSP_mask::test_art_row(int r, bool signal) { TSheet_field& sf = sfield(F_ARTICOLI); int c; if (!TRiga_articolo::find_block(sf, r, c, r)) return TRUE; else { TToken_string & curr_row=sf.row(r); TToken_string & constr_row=sf.row(c-1); // setta la priorita' su tutte le righe del blocco const int prior =curr_row.get_int(sf.cid2index(F_PRIORITA)); const int old_prior=constr_row.get_int(sf.cid2index(F_PRIORITA)); if (old_prior != prior) { for (int i = c-1; i<=r; i++) { sf.row( i ).add(prior , sf.cid2index(F_PRIORITA)); sf.force_update(i); } } } return do_test_art_row(r, c, signal); } bool TMSP_mask::test_load_row(int r, bool signal) { TSheet_field& sf = sfield(F_LINEE); const int narticoli=sf.items(); if (narticoli<=r) return TRUE; // Calcola l'indice c della riga con la capacita' const int b0 = sf.cid2index(F_LBUCKET0); const int b1 = sf.cid2index(F_LBUCKET1); for (int c = r; c > 0; c--) { if (sf.cell_disabled(c, b1)) break; } // Calcola l'indice r della riga totale di linea if (r == c) r++; TToken_string &row=sf.row(c); real capacita, carico; for (int b = 1; b < LAST_BUCKET; b++) { capacita=real(sf.cell(c, b0+b*2)); carico=real(sf.cell(r, b0+b*2)); if (signal && capacita < carico) { TString err; const int bucket_size = days_per_bucket(); const TDate d = get_date(F_DADATA) + long(bucket_size * (b-1)); err << "Riga " << c+1 << ": capacita' superata al " << d; beep(); signal =FALSE; xvt_statbar_set(err); } } if (signal) xvt_statbar_set(""); return signal; } void TMSP_mask::salva_documenti() { TSheet_field& sf = sfield(F_ARTICOLI); const int b0 = sf.cid2index(F_BUCKET0); const int b1 = sf.cid2index(F_BUCKET1); const int um = sf.cid2index(F_UM); const TDate date_to = get(F_ADATA); TPlan_docs doc_rows(get(F_NUM_PLAN), get(F_TIPO_PLAN), get(F_RIGA_PLAN)); const TDate from = get(F_DADATA); const int bucket_size = days_per_bucket(); const bool number_by_cli = get_bool(F_NUMBERBYCLI); for (int b = 1; b < LAST_BUCKET; b++) { const TDate data = from + long(bucket_size * (b-1)); if (data > date_to) break; const int anno = data.year(); FOR_EACH_SHEET_ROW(sf, r, row) if (!sf.cell_disabled(r, b1)) { TMSP_line& line = *_articles.find(*row, _livelli, TRUE); const real& old = line.qta(b); const real val = row->get(b0 + b*2); if ((get(F_DA_MAGAZZ).empty() || line.codmag() >= get(F_DA_MAGAZZ)) && (get(F_A_MAGAZZ).empty() || line.codmag() <= get(F_A_MAGAZZ)) && (get(F_DA_IMPIANTO).empty() || line.codimp() >= get(F_DA_IMPIANTO)) && (get(F_A_IMPIANTO).empty() || line.codimp() <= get(F_A_IMPIANTO))) { if (old != val) { long numdoc = 0L; int numrig = 0; if (!line.riga(b, numdoc, numrig)) { if (number_by_cli) numdoc = line.cliente() * 100L; numdoc += data.week(); } const real incr = val-old; TRiga_documento& riga = doc_rows.add_to_row(anno, numdoc, numrig, incr); if (riga.get(RDOC_CODART).empty()) { riga.put(RDOC_CODART, line.codice()); riga.put(RDOC_CODARTMAG, line.codice()); riga.put(RDOC_LIVELLO, line.livgiac()); riga.put(RDOC_CHECKED, "X"); riga.put(RDOC_CODMAG, line.codmag()); riga.put(RDOC_LINEA, line.codlin()); riga.put(RDOC_IMPIANTO, line.codimp()); riga.put(RDOC_UMQTA, row->get(sf.cid2index(F_UM))); riga.put(RDOC_DATACONS, data); riga.put(RDOC_DESCR, row->get(sf.cid2index(F_DESCART))); TRectype& doc = (TRectype&)riga.doc().head(); if (doc.get(DOC_DATACONS).empty()) { doc.put(DOC_DATACONS, data); doc.put(DOC_TIPOCF, "C"); doc.put(DOC_CODCF, line.cliente()); } doc.put(DOC_DATADOC, from); } } } } doc_rows.flush(data); } } // copia i dati della riga al di fuori delle informazioni dei bucket void TMSP_mask::copy_sheet_row(TToken_string & newrow, const TToken_string & row) { const TSheet_field& sf = sfield(F_ARTICOLI); const int b0 = sf.cid2index(F_BUCKET0); TString str; for (int i = 0; i < b0; i++) { row.get(i, str); newrow.add(str, i); } } bool TMSP_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) { const char * confirm_msg="Le attuali %d linee di articoli 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_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 F_TIPI_ORC: case F_TIPI_PLA: 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()) { bool ok; if (o.dlg()==F_TIPI_PLA) ok=test_tipodoc_num(sfield(F_NUM_PLA), s ); else ok=test_tipodoc_num(sfield(F_NUM_ORC), s ); return ok; } return error_box("E' necessario inserire almeno una riga"); } break; case F_NUM_PLAN: if (e == fe_close) { const TSheet_field& s = sfield(F_NUM_PLA); FOR_EACH_SHEET_ROW_BACK(s, r, row) if (o.get() == row->get(s.cid2index(F_NUMERAZ))) return TRUE; return error_box("Numerazione non inclusa tra quelle di planning"); } break; case F_TIPO_PLAN: if (e == fe_close) { const TSheet_field& s = sfield(F_TIPI_PLA); FOR_EACH_SHEET_ROW_BACK(s, r, row) if (o.get() == row->get(s.cid2index(F_TIPO))) return TRUE; return error_box("Tipo documento non incluso tra quelli di planning"); } break; case F_ARTICOLI: { TSheet_field& s = (TSheet_field&)o; switch(e) { case se_query_add: return jolly > 1 && s.items() > 0; case se_notify_add: if (jolly > 0) { const TToken_string& prev = s.row(int(jolly-1)); TToken_string& curr = s.row(int(jolly)); copy_sheet_row(curr,prev); } break; case se_leave: test_art_row((int)jolly); break; default: break; } } break; case F_MSP_RESORT: if (e==fe_button) sort_sheet(); break; case F_LINEE: { TSheet_field& s = (TSheet_field&)o; switch(e) { case se_query_add: return FALSE; case se_leave: test_load_row((int)jolly); break; } } break; case F_CRP_RESORT: break; case F_BUCKET1: case F_BUCKET2: case F_BUCKET3: case F_BUCKET4: case F_BUCKET5: case F_BUCKET6: case F_BUCKET7: case F_BUCKET8: case F_BUCKET9: case F_BUCKET10: case F_BUCKET11: case F_BUCKET12: if (e == fe_modify) { const TSheet_field& s = sfield(F_ARTICOLI); TMask & m=s.sheet_mask(); TMask_field &fprice=m.field(o.dlg()+1); real qta(o.get()); real price(fprice.get()); if (!qta.is_zero() && price.is_zero()) find_price(get(F_TIPOCV),get(F_CODCONDV),get(F_CATVEN_CV), m.get(F_TIPOCF_SHEET), m.get_long(F_CLIENTE), m.get(F_ARTICOLO), qta , price); fprice.set(price.string()); } case DLG_CANCEL: if (e == fe_button) if (jolly == 0L) { TSheet_field& s = sfield(F_ARTICOLI); int lineeart=s.items(); if (lineeart==0 || yesno_box(confirm_msg,lineeart)) { enable(-1); clear_sheets(); } else return FALSE; } break; case DLG_ELABORA: if (e == fe_button && check_fields()) { TSheet_field& s = sfield(F_ARTICOLI); int lineeart=s.items(); if (lineeart==0 || yesno_box(confirm_msg,lineeart)) { clear_sheets(); if (carica_documenti()) { fill_sheet(); enable(DLG_SAVEREC, s.items() > 0); enable(-1, s.items() == 0); s.set_focus(); on_field_event((TOperable_field&)field(F_SHOWPRICES), fe_modify, jolly ); on_field_event((TOperable_field&)field(F_MSP_SORT), fe_init, jolly ); } } } break; case F_CANCEL_MSP: if (e == fe_button) { remove_propose(TRUE); } break; case F_RECALC_MSP: if (e == fe_button && check_fields()) { remove_propose(); const bool useextralines=get_bool(F_USENONSTDLIN_MSP); const bool useextrahours=get_bool(F_USEEXTRAHRS_MSP) ; set(F_USENONSTDLIN_CRP ,useextralines ? "X" : ""); set(F_USEEXTRAHRS_CRP ,useextrahours ? "X" : ""); if (get(F_RECALC_TYPE)=="1") propose_1stJIT(_first_fit_logic,TRUE,TRUE); else if (get(F_RECALC_TYPE)=="1_INFH") propose_1stJIT(_first_fit_logic,TRUE,FALSE); else if (get(F_RECALC_TYPE)=="1_INF") propose_1stJIT(_first_fit_logic,FALSE,FALSE); else if (get(F_RECALC_TYPE)=="JIT") propose_1stJIT(_JIT_logic,TRUE,TRUE); else if (get(F_RECALC_TYPE)=="JIT_INFH") propose_1stJIT(_JIT_logic,TRUE,FALSE); else if (get(F_RECALC_TYPE)=="JIT_INF") propose_1stJIT(_JIT_logic,FALSE,FALSE); // aggiorna lo sheet delle capacita' on_field_event((TOperable_field&)field(F_LOADTYPE), fe_modify, jolly ); sfield(F_ARTICOLI).set_focus(); } break; case F_RECALC_CRP: if (e == fe_button && check_fields()) { if (capacity_review(get_bool(F_USENONSTDLIN_CRP) ,get_bool(F_USEEXTRAHRS_CRP) )) { on_field_event((TOperable_field&)field(F_LOADTYPE), fe_modify, jolly ); sfield(F_LINEE).set_focus(); } } break; case F_CHECK_MSP: if (e == fe_button) { check_articles(); } break; case F_CHECKLINE: if (e == fe_button) { const TSheet_field& s = sfield(F_ARTICOLI); TMSPCheck_mask cm(this); cm.disable(-G_MOVIMENTO); int curr_row=s.selected(); const bool is_disabled=s.cell_disabled(curr_row,F_BUCKET1-FIRST_FIELD) ; const bool is_constr=(is_disabled && curr_row>0 && s.cell_disabled(curr_row-1,F_BUCKET1-FIRST_FIELD)); if (is_disabled && !is_constr) curr_row++; cm.gopos(curr_row, 1); cm.run(); cm.gopos_mainmask(); } break; case F_SHOWPRICES: if (e == fe_modify) { TSheet_field& s = sfield(F_ARTICOLI); const bool on = get_bool(F_SHOWPRICES); for (int b=LAST_BUCKET ; b>=0 ; b--) { s.set_column_width(F_BUCKET0+1+b*2-FIRST_FIELD, on ? 140 : 0); s.enable_column(F_BUCKET0+1+b*2-FIRST_FIELD, on && b>0 && b < LAST_BUCKET); // ^^^^- da sostituire con un piu' elegante (ma non implementato): s.show(F_BUCKET0+1+b*2-FIRST_FIELD,on); } s.force_update(); } case F_CHECK_CRP: if (e == fe_button) { check_capacities(); } break; case F_SHOWPERC: if (e == fe_modify) on_field_event((TOperable_field&)field(F_LOADTYPE), fe_modify, jolly ); break; case F_LOADTYPE: if (e == fe_modify) { // ************* // fill sheet of capacities const bool as_percentage=get_bool(F_SHOWPERC); const bool show_human=*get(F_LOADTYPE)=='H'; TSheet_field& sl = sfield(F_LINEE); sl.destroy(); //TSheet_field& su = sfield(F_TESTE); //su.destroy(); const long max_capacities=_capacities.items(); for (long c=0; csfield(F_ARTICOLI); int curr_row=last_row(); while (sf.cell_disabled(curr_row,F_BUCKET1-FIRST_FIELD) ) curr_row++; sf.select(curr_row,TRUE); _main_mask->set_focus_field(sf.dlg()); sf.set_focus_cell(curr_row, last_col()*2+F_BUCKET0); } TMSPCheck_mask::TMSPCheck_mask(TMSP_mask * main_mask, const char *m,TSheet_field * s): TAutomask(m), _main_mask(main_mask) , _sheet(s) { _last_col=_last_row=_constr_row=-1; go_top(); } TMSPCheck_mask::TMSPCheck_mask(TMSP_mask * main_mask): TAutomask("mr2200b.msk"), _main_mask(main_mask), _sheet(&main_mask->sfield(F_ARTICOLI)) { _last_col=_last_row=_constr_row=-1; go_top(); } //valori di ritorno: // 0 : nessun errore // 1 : esistono modifiche // 2 : esistono nuove proposta // 4 : giacenza media superiore al voluto // 8 : sotto stock int TMSPCheck_mask::fill_mask(const bool show_fields) { int error=0; if (_last_col==_col && _last_row==_row) return error; const bool is_constr=is_constraint(_row); TToken_string &row=_sheet->row(_row); long codcli ; TCodice_articolo codart; TString16 liv, mag, codimp, codlin, um; _main_mask->msprow2codes(row,codcli,codart,liv,mag,codimp, codlin, um); if (_last_row!=_row) { _constr_row=find_constraint(_row); set(FC_RIGA,_row+1); set(FC_CODCLI,codcli,TRUE); set(FC_CODART,codart,TRUE); set(FC_CODIMP,codimp,TRUE); set(FC_CODLIN,codlin,TRUE); set(FC_CODMAG,mag.left(3),TRUE); set(FC_CODDEP,mag.mid(3),TRUE); } set(FC_BUCKET,_col); TDate data_buck(_main_mask->get_date(F_DADATA)); data_buck += (_col)*_main_mask->days_per_bucket()-1; set(FC_DATE2,_col==LAST_BUCKET ? "" : data_buck.string()); data_buck -= _main_mask->days_per_bucket()-1; set(FC_DATE1,_col==0 ? "" : data_buck.string()); set(FC_IS_VINCOLO,is_constr ? "X" : " "); field(FC_IS_VINCOLO).on_hit(); if (!is_constr) { real art_per_buck(row.get(F_BUCKET0 + _col*2 - FIRST_FIELD)); set(FC_FINAL_VALUE,art_per_buck.string()); TMRP_line* new_article = _main_mask->find_propose(codcli, codart,liv,codimp,codlin,mag); TMSP_line* old_article = _main_mask->find_article(codcli, codart,liv,codimp,codlin,mag); real old_value=ZERO; real new_value=ZERO; if (old_article) { old_value=old_article->qta(_col); } if (new_article) { TMRP_time t(data_buck,0,"",""); TMRP_record& mrprec = new_article->record(t); new_value=mrprec.sched_receipts(); if (new_value > ZERO) error|=2; } set(FC_STARTING_VALUE,old_value.string()); set(FC_PROPOSED_VALUE,new_value.string()); show(FC_PROPOSED_VALUE,!new_value.is_zero()); show(FC_STARTING_VALUE,!old_value.is_zero()); if (old_value != art_per_buck) error|=1; // mostra i campi hide(FC_GIAC); hide(FC_UNDERSTOCK); } if (_constr_row>=0) { // calcola il sotto stock real stock(_sheet->cell(_constr_row,F_BUCKET0 + _col*2 - FIRST_FIELD)); bool stockbreak=stockdays_per_bucket(); real prev_stock(_sheet->cell(_constr_row,F_BUCKET0 + 2*_col - 1 - FIRST_FIELD)); real delivery(_sheet->cell(_constr_row-1,F_BUCKET0 + 2*_col - FIRST_FIELD)); stock=stock-prev_stock+delivery; if (stock > ZERO) { real residuo=stock; while (residuo > ZERO && (_col + bucket)cell(_constr_row-1,F_BUCKET0 + 2*_col + bucket - FIRST_FIELD); if (delivery < prev_stock) { prev_stock-=delivery; delivery=ZERO; } else { delivery-=prev_stock; prev_stock=ZERO; } residuo-= delivery; if (residuo < ZERO) residuo=ZERO; giorni+= residuo; bucket++; } giorni = buck_days*giorni / stock; const int maxdays(_main_mask->get_int(F_MAXGIORNIGIAC)); overgiac=(giorni.integer() > maxdays); error|=overgiac ? 4 : 0; } } set(FC_GIORNIGIAC,giorni.string()); set(FC_OVERGIORNIGIAC,giorni.string()); show(FC_GIORNIGIAC, !overgiac); show(FC_OVERGIORNIGIAC,overgiac); } return error; } void TMSPCheck_mask::fix_actual_pos() { _last_col=_col; _last_row=_row; } void TMSPCheck_mask::gopos(int row, int col) { _row=row; _col=col; fill_mask(); fix_actual_pos(); } bool TMSPCheck_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) { if (e==fe_button) { const int dlg=o.dlg(); bool redo=TRUE; const TString4 old_movefilter(get(FC_MOVEFILTER)); const TString4 new_movefilter(get_int(FC_FINDFILTER) & 1 ? "" : "C"); switch (dlg) { case FC_UP_BAD: set(FC_MOVEFILTER,new_movefilter); if (redo = move_to(FC_UP)) _col = LAST_BUCKET+1; case FC_LEFT_BAD: set(FC_MOVEFILTER,new_movefilter); while (redo) { while (move_to(FC_LEFT)) { int err=fill_mask() ; fix_actual_pos(); if (err & get_int(FC_FINDFILTER)) { set(FC_MOVEFILTER,old_movefilter); return TRUE; } } if (redo = move_to(FC_UP)) _col = LAST_BUCKET+1; } message_box("Ricerca terminata"); break; case FC_DOWN_BAD: set(FC_MOVEFILTER,new_movefilter); if (redo = move_to(FC_DOWN)) _col = -1; case FC_RIGHT_BAD: set(FC_MOVEFILTER,new_movefilter); while (redo) { while (redo && move_to(FC_RIGHT)) { int err=fill_mask() ; fix_actual_pos(); if (err & get_int(FC_FINDFILTER)) { set(FC_MOVEFILTER,old_movefilter); return TRUE; } } if (redo = move_to(FC_DOWN)) _col = -1; } message_box("Ricerca terminata"); break; default: move_to(dlg); fill_mask(); fix_actual_pos(); } set(FC_MOVEFILTER,old_movefilter); } return TRUE; } int TMSPCheck_mask::find_constraint(int row) { while (row >=0 ) { if (is_constraint(row)) return row; row--; } return -1; } bool TMSPCheck_mask::is_constraint(int row) { return (row > 0 && _sheet->cell_disabled(row, F_BUCKET1-FIRST_FIELD) && _sheet->cell_disabled(row-1, F_BUCKET1-FIRST_FIELD)); } bool TMSPCheck_mask::is_article(int row) { return !_sheet->cell_disabled(row, F_BUCKET1-FIRST_FIELD); } bool TMSPCheck_mask::move_to(int dlg) { const bool constraints=*get(FC_MOVEFILTER)!='A'; const bool articles=*get(FC_MOVEFILTER)!='C'; CHECK(constraints||articles, "N'do cazzo muovo?"); const int max_rows=_sheet->items(); bool skip_row=TRUE; int old_row=_row; switch (dlg) { case FC_DOWNLEFT: case FC_DOWNRIGHT: case FC_DOWN: while (_row < max_rows-1) { _row++; if (constraints && is_constraint(_row) || articles && is_article(_row)) break; } if (dlg==FC_DOWNRIGHT) _col++; if (dlg==FC_DOWNLEFT) _col--; break; // se non sono sulla linea giusta, torno indietro case FC_UPRIGHT: case FC_UPLEFT: case FC_UP: while (_row >0 ) { _row--; if (constraints && is_constraint(_row) || articles && is_article(_row)) break; } if (dlg==FC_UPRIGHT) _col++; if (dlg==FC_UPLEFT) _col--; break; case FC_LEFT: _col--; break; case FC_RIGHT: _col++; break; } if (!((articles && is_article(_row) ) || (constraints && is_constraint(_row)))) _row=old_row; check_pos_range(); if (_last_col==_col && _last_row==_row) return FALSE; return TRUE; } void TMSPCheck_mask::check_pos_range() { int max_cols=min(get_int(FC_MAXBUCK2CHECK),LAST_BUCKET); if (max_cols==0) max_cols=LAST_BUCKET; const bool constraints=*get(FC_MOVEFILTER)!='A'; const int max_rows=_sheet->items(); if (_row>=max_rows) _row=max_rows-1; if (_row< (constraints ? 1 : 2) ) _row++; if (_col>=max_cols) _col=max_cols; if (_col<1) _col=0; } /////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////// TCRPCheck_mask::TCRPCheck_mask(TMSP_mask * main_mask): TMSPCheck_mask(main_mask, "mr2200c.msk", & main_mask->sfield(F_LINEE)) { } void TCRPCheck_mask::check_pos_range() { const bool constraints=*get(FC_MOVEFILTER)!='A'; const int max_rows=_sheet->items(); if (_row>=max_rows) _row=max_rows-1; if (_row< (constraints ? 0 : 1) ) _row++; if (_col>=LAST_BUCKET) _col=LAST_BUCKET-1; if (_col<1) _col=1; } bool TCRPCheck_mask::is_constraint(int row) { return (row > 0 && !_sheet->cell_disabled(row, F_LBUCKET1-FIRST_FIELD) && _sheet->cell_disabled(row-1, F_LBUCKET1-FIRST_FIELD)); } bool TCRPCheck_mask::is_article(int row) { return (*_sheet->row(row).get(F_CODARTCRP-FIRST_FIELD)>' '); } int TCRPCheck_mask::find_constraint(int row) { const int max=max_rows(); while (row < max) { if (is_constraint(row)) return row; row++; } return -1; } int TCRPCheck_mask::fill_mask(const bool show_fields) { int error=0; if (_last_col==_col && _last_row==_row) return error; const bool is_constr=is_constraint(_row); TSheet_field& sf = _main_mask->sfield(F_LINEE); TToken_string &row=sf.row(_row); long codcli ; TCodice_articolo codart; TString16 liv, codimp, codlin, um; _main_mask->crprow2codes(row,codimp, codlin,codcli,codart,liv,um); if (_last_row!=_row) { _constr_row=find_constraint(_row); set(FC_RIGA,_row+1); set(FC_CODIMP,codimp); set(FC_CODLIN,codlin); set(FC_CODCLI,codcli); set(FC_CODART,codart); } set(FC_BUCKET,_col); TDate data_buck(_main_mask->get_date(F_DADATA)); data_buck += (_col)*_main_mask->days_per_bucket()-1; set(FC_DATE2,data_buck.string()); data_buck -= _main_mask->days_per_bucket()-1; set(FC_DATE1,data_buck.string()); set(FC_IS_VINCOLO,is_constr ? "X" : " "); field(FC_IS_VINCOLO).on_hit(); if (!is_constr) { // articolo real art_load(_sheet->cell(_row ,F_LBUCKET1 + _col - 1 - FIRST_FIELD)); set(FC_ART_LOAD,art_load.string()); } if (_constr_row>=0) { // vincolo real load(_sheet->cell(_constr_row ,F_LBUCKET1 + _col - 1 - FIRST_FIELD)); set(FC_LOAD,load.string()); show(FC_LOAD,load>ZERO); real capacity(_sheet->cell(_constr_row -1 ,F_LBUCKET1 + _col - 1 - FIRST_FIELD)); const bool positive=capacity>ZERO; set(FC_CAPACITY,capacity.string()); show(FC_CAPACITY,positive); capacity-=load; const bool out=capacity