<?php
/*
  Les tables ont changé dans l'interprétation de resType/besType :
	new = 1=ferme, 4=prev et ajout d'un champ resOrig/besOrig qui reprend l'ancien rôle
	Valeurs possibles de 
		- resOrig	 : 'OA'=ordre achat,       'OF'=ordre fab.,                         'OP'=ressce prévi. calculée 
		- besOrig	 : 'OV'=ordre vente (cde), 'BF'=besoin ordre fab. issus de la nom., 'BP'=besoin prévi. calculé  
	le champ resType/besType nous dit si l'ordre/besoin est 0=ferme et manuel ou 4=prévisionnel et calculé par moi même (c'est le programme qui parle! allô?)
	
	Les besoins/ressources peuvent êtres 
		- externes, fermes ou déjà lancés ou prévisionnels de sources externes (cdes client prévi) : OV, OF, OA
			un ordre ferme ne peut être modifié (OV, OF ou OA) cependant, ils peuvent être annulé manuellement par le service concerné (gescom, client, fab, achats) certaines conditions sont alors à respecter
		- calculés (et donc prévisionnels) : BP, OP avec champ type=4
			un besoin/ressource calculé est recalculé à chaque calcul des besoins.
		- manuels, fermes ou prévisionnels  (besoin calculé dont on a déplacé la date, dont on a modifié la quantité manuellement ): BP ou OP avec un champ type=0
			ces derniers ne peuvent être modifiés par le calcul mais peuvent être modifiés manuellement.
*/
/*
	états des commandes : 
	0= nouvelle=devis, 1=commande ferme, 2=livrée, 4=facturée, 8=payée, 16=terminée, 32=annulée
		pflc **	pflC **	pfLc **	pfLC **		
		0000  0	0001  1	0010  2	0011  3	
		0100  4	0101  5	0110  6	0111  7	Fac
		1000  8	1001  9	1010 10	1011 11		Pay = payée non facturée = impossible
		1100 12	1101 13	1110 14	1111 15	Fac	Pay
	0=new/devis, 1=invoice, 2=part.shipped, 3=shipped, 4=billed, 7=done, 9=cancel
	A FAIRE : faire la gestion du stock : 
	-	afficher si le stock est suffisant ou non dans les lignes et, 
		dans ce cas, envoyer un petit script qui interdit l'expédition globale 
		(la facturation devra rester possible si on peut facturer avent d'expédier ...)
	- 	inscrire les mouvements de stock correspondants et, 
		si la base de données est adaptée, modifier le stock dynamique courant de chaque article expédier.
		
	A VENIR : 
		- 	gérer les expéditions en plusieurs colis
		-	gérer les expéditions partielles
*/

// ==============================================================================================================
// fonction de calcul des besoins avec la méthode MRP.
// ==============================================================================================================
/* Principe,
	On calcule tous les besoins fermes issus des commandes en cours (non livrées) et des réquisition de produits issus des OF.
	A partir de là, calcule un besoin net et on propose des ordres prévisionnels
	Ces ordres prévisionnels sont convertis en besoins prévisionnels à l'aide des nomenclatures. on descend alors d'un niveau
	Ces besoins prévisionnels serviront au calcul suivant ....
*/
// _initStock() 		Ajoute une ligne bidon (BI) dans les besoins afin de déclencher le calcul du stock mini
function _initStock($level=0){
// la ligne BI n'est pas prise en compte dans le calcul du besoin mais peut déclencher un OP ...
global $connexion;
	$insert=false;	// on n'a rien ajouté dans les besoins
	
	$STartSQL="Select artCode, stockInit, artStockMini FROM article	"; // besoins stock	
	$STartSQL_id=DB_ouvrirRequete($STartSQL, $connexion);
	$STartSQLNbRow=mysql_num_rows($STartSQL_id);
	//echo "DBG : initStock : $STartSQLNbRow rows".mysql_error();
	
	$besDate=COM_aujourdhui(); // date = aujourd'hui
	while ($t_ST=DB_lireLigne($STartSQL_id)){
		$stockCourant=$t_ST['stockInit'];
		if ($stockCourant<$t_ST['artStockMini']){ // déficit de stock
			$artCode=$t_ST['artCode'];
			//$besDate=$besDate;
			$besQte=$t_ST['artStockMini']-$stockCourant;
			$besSQL= "INSERT INTO mrpbesoins (besOrig, artCode, besDate, besQte, besRef, besType, besLevel) VALUES 
				('BI', '$artCode', '$besDate', $besQte, 'BI.$level-".$t_ST['artCode']."', 4, $level);";
			DB_executerRequete($besSQL, $connexion);
			//echo mysql_error();
			$insert=true;	// on a ajouté quelque chose
		}
	}
	return $insert;
}
// _initBesoins()		met en place les besoins fermes dans les besoins : BB= OV ou BF
function _initBesoins($level=0){
/* chaque ligne de commande ou de besoin d'of devient un besoin ferme */
global $connexion;
	$insert=false;	// on n'a rien ajouté dans les besoins

// A MARVELOUS QUERY !!!
// bcc : Besoins issus des commandes client en cours (toutes commandes non livrées )
	$BBccSQL="Select lignecdeclientart.artCode AS artCode, cdeDate AS date, qte, concat('OV.$level-',lignecdeclientart.artCode,'/',lignecdeclientart.cdeNum) as besRef
	FROM article, lignecdeclientart, cdeclient 
	WHERE article.artCode=lignecdeclientart.artCode
	AND lignecdeclientart.cdeNum=cdeclient.cdeNum
	AND ligneEtat in (0,1,4,5,8,9,12,13) 
	"; // besoins client	
	// AND artTypeCode in ('F', 'N') // pas besoin, seuls des articles vendus sont concernés

// bof : besoins issus des ordres de fabrication
	/* Ici, on utilise la date de début de l'opération de l'OF pour caler le besoin. 
	   Dans la partie laissée active (+loin) on utilise la date de début de l'OF, comme si toutes les matières devrait toutes être présentes en début d'OF
		$BBofSQL="SELECT ofComp_artCode AS besArtCode, ofGamme.opDateDeb AS besDate, (ofQteTheo) AS besQte, 'BF' AS besOrig
		FROM ofNomenclature, ofGamme
		WHERE ofNomenclature.ofNum=ofGamme.ofNum
		AND ofNomenclature.opNum=ofGamme.opNum "; // besoins issus des OF
	*/
	$BBofSQL="Select ofComp_artCode AS artCode, ofDateDeb AS date, ofQteTheo AS qte, concat('BF.$level-',ofnomenclature.ofComp_artCode,'/',ofnomenclature.ofNum) as besRef
	FROM article, ofnomenclature, ordfab
	WHERE article.artCode=ofnomenclature.ofComp_artCode
	AND ofnomenclature.ofNum=ordFab.ofNum 
	AND artTypeCode in ('F', 'N')
	"; // besoins issus des OF

// 	Assemblage de la requête	
	$BBSQL=$BBccSQL." UNION ".$BBofSQL." ORDER BY artCode, date ;";
	//echo "<br>DBG : sql=$BBSQL<br>";
	
// exécution et traitement de la requête : écriture de chaque besoin ferme dans mrpBesoins
	$BBSQL_id=DB_ouvrirRequete($BBSQL, $connexion);
	$BBSQLNbRow=mysql_num_rows($BBSQL_id);
	//echo "DBG : initBesoins : $BBSQLNbRow rows".mysql_error();

	while ($t_BB=DB_lireLigne($BBSQL_id)){
		$artCode=$t_BB['artCode'];
		$besDate=$t_BB['date'];
		$besQte=$t_BB['qte'];
		$besRef=$t_BB['besRef'];
		$besType=1; // ordre ferme
		$besSQL= "INSERT INTO mrpbesoins (besOrig, artCode, besDate, besQte, besRef, besType, besLevel) VALUES
			('BB', '$artCode', '$besDate', $besQte, '$besRef', $besType, $level);";
		DB_executerRequete($besSQL, $connexion);
		//echo "<br>".mysql_error();
		//echo "<br>DBG : 'BB', art='$artCode', date='$besDate', qte=$besQte, ref='$besRef', type=$besType, level=$level";
		$insert=true;	// on a ajouté quelque chose
	}
	return $insert;
}
// _initRessources()	met en place les ordres lancés dans les ressources : OL= OA ou OF
function _initRessources($level=0){
global $connexion;
	$insert=false;	// on n'a rien ajouté dans les besoins

/* chaque ligne de commande achat ou d'of devient une ressource ferme */
// OL Ordres Lancés = ressources réelles attendues ---------------------------------------------------------------------------
	// OA : Ordres d'achat lancés = ressource commandes fournisseur (non encore livrées !!! A FAIRE)
	// note : on prend tous les articles, quel que soit leur type
	$RBoaSQL="SELECT lignecdefourart.artCode AS artCode,  cdeDate AS date, qte , concat('OA.$level-',lignecdefourart.artCode,'/',lignecdefourart.cdeNum) AS resRef 
	FROM lignecdefourart, cdefour	WHERE lignecdefourart.cdeNum=cdeFour.cdeNum 	"; // commandes fournisseur

	// OF : Ordres de fabrications lancé = ressource ordre de fabrication en cours (rem : non finis = quantité bonne restante à produire, mais on n'a pas l'info simplement ... A VOIR)
	$RBofSQL="SELECT ordfab.artCode AS artCode, ofDateFin AS date, ofQte AS qte, concat('OF.$level-',ordfab.artCode,'/',ordfab.ofNum) AS resRef
	FROM ordfab 	"; // ordres de fabrication

	$RBSQL=$RBoaSQL." UNION ".$RBofSQL." ORDER BY artCode, date ;";	// 	Assemblage de la requête	
	//echo "<br>DBG : sql=$RBSQL<br>";

	$RBSQL_id=DB_ouvrirRequete($RBSQL, $connexion);	// exécution et traitement de la requête : écriture de chaque besoin ferme dans mrpBesoins
	$RBSQLNbRow=mysql_num_rows($RBSQL_id);
	//echo "DBG : initRessources : $RBSQLNbRow rows ".mysql_error();

	while ($t_RB=DB_lireLigne($RBSQL_id)){
			$artCode=$t_RB['artCode'];
			$resDate=$t_RB['date'];
			$resQte=$t_RB['qte'];
			$resRef=$t_RB['resRef'];
			$resType=1; // ordre ferme
			$resSQL= "INSERT INTO mrpressources (resOrig, artCode, resDate, resQte, resRef, resType, resLevel) VALUES 
				('OL', '$artCode', '$resDate', $resQte, '$resRef', $resType, $level);";
			DB_executerRequete($resSQL, $connexion);
			//echo mysql_error();
			$insert=true;	// on a ajouté quelque chose
	}
	return $insert;
}
// _calculBN()	calculer les Besoins Nets et Ordres Prévisionnels (OP = ressources nettes théoriques) pour le niveau demandé.
function _calculBN($level=0) {
// A FAIRE : Le calcul devra tenir compte de la notion de lot économique.
// A VOIR : si on ne cumule pas les besoins par date pour en faire une seule ressource, on devra créer un fichier des BN et un fichier des OP qui eux seront groupés.
global $connexion;
	$insert=false;	// on n'a rien ajouté dans les besoins
	// BB mrp calculés : y en a pas encore ... (ressources théoriques calculées) -----------------------------------------------------------
	$BBmrpSQL="Select * FROM mrpBesoins  WHERE besLevel=$level ORDER BY artCode, besDate"; // besoins brut existants	
	$BBmrpSQL_id=DB_ouvrirRequete($BBmrpSQL, $connexion);
	$BBmrpSQLNbRow=mysql_num_rows($BBmrpSQL_id);
	//echo "DBG : calculBN : $BBmrpSQLNbRow rows ".mysql_error();
	//echo "<br>DBG : BBmrpSQL = $BBmrpSQL";

/* 	Bon! on a une table qui a deux ruptures : l'article et la date.
	Le problème c'est qu'on peut avoir plusieurs lignes de BB ou OL pour un même article et une même date ...
	Il faut donc les additionner jusqu'à ce qu'on passe à la date (ou l'article suivant). A ce moment là seulement, on peut écrire la ligne dans le code html.
	
	L'algo ressemble donc à ceci : 
	TQ non EOT Faire :
		Si on change de date ou d'article, on écrit la ligne en cours puis RAZ des compteurs
		Si on a un nouvel article, l'article devient l'article en cours et on recherche les stocks mini et init puis RAZ de la date.
		Si on a une nouvelle date, la date devient la date en cours.
		On accumule les BB et le OL
	Fin TQ
	Si on a une ligne en cours, on l'écrit
*/
	$currentArtCode="";	$currentDate="";	$compNbr=0;
	$notFirstLine=false; // pour éviter de faire une ligne vide au début
	//$opOpen=false;
	$STQty=$BBQty=$BPQty=$OLQty=$BNQty=$OPQty=$BIQty=$SPQty=0;
	// les besoins sont condensés par date et écrits comme des propositions de ressources.
	while ($t_BB=DB_lireLigne($BBmrpSQL_id)){
		// D'abord terminer la ligne en cours si on change de code article ou de date
		if ($notFirstLine && ($t_BB['artCode']!=$currentArtCode || $t_BB['besDate']!=$currentDate)){ // nouvelle date ou nouvel article, finir la ligne en cours
			echo "<br>DBG :----- end art/date ($currentArtCode), date($currentDate) somme(qty)=".($BIQty+$BBQty+$BPQty+$OLQty+$OPQty);
			if ($BIQty+$BBQty+$BPQty+$OLQty+$OPQty>0){ // Si il y a une quantité à écrire dans la ligne en cours
				// calcul des ordres prévisionnels (ce sont eux qui sont à planifier, pas le besoin net ...)
				// ST ------	reprise du stock de la ligne précédente (si même article ...)
					$STQty=$SPQty;	// attention, ce SPQty est celui de la ligne précédente !!!
					// BB ------ cumul des commandes et besoins fermes (OV+BF) 
					// BP ------ cumul des besoins prévisionnels (BP) 
					// OL ------ cumul des ordres fermes lancés (OF+OA) 
					// BN ------ on ne tient pas compte des notions de lot ou de multiple ; 
					// le stock réellement dispo est le stock au-delà du stock mini (sécu ou point de commande) donc -(STQty-stockMini)
					$BNQty=$stockMini+$BBQty+$BPQty-$OLQty-$OPQty-$STQty;
					if ($BNQty<0) $BNQty=0;
				// OP ------	calcul des ordres prévisionnels
					$newOPQty=$BNQty;	// pour l'instant pas de calcul, on reprend le BN
				// SP ------ 	calcul du stock prévisionnel avec les besoins calculés pour la prochaine rotation ...
					$SPQty=$STQty+$OLQty+$OPQty-$BBQty-$BPQty+$newOPQty;
				
				// si un besoin existe alors écrire l'ordre prévisionnel à lancer dans le fichier mrpRessources
				echo " newOP=$newOPQty (STMini=$stockMini ST=$STQty BB=$BBQty BP=$BPQty OL=$OLQty BN=$BNQty oldOP=$OPQty).";
				if ($newOPQty>0){
					$resRef=$besOrig.".".$level."-".$currentArtCode;
					$besSQL= "INSERT INTO mrpressources (resOrig, artCode, resDate, resQte, resRef, resType, resLevel) 
						VALUES ('OP', '$currentArtCode', '$currentDate', $newOPQty, '$resRef', 4, $level);";
					DB_executerRequete($besSQL, $connexion);
					// echo "DBG : ".mysql_error();
					$insert=true;	// on a ajouté quelque chose
					echo " >> line Written.";
				}
				// RAZ des compteurs
				$STQty=$BBQty=$BPQty=$OLQty=$BNQty=$OPQty=$newOPQty=0; // $SPQty ne doit pas être RAZ, on en a besoin au prochain tour ...
			}
		}
		$notFirstLine=true; // ok maintenant on peut écrire une (fin de) ligne
		
		if ($t_BB['artCode']!=$currentArtCode){  // NEW item, écrire la première ligne du nouvel article [ok au 04/01/2015]
			$currentArtCode=$t_BB['artCode'];				// article courant = nouvel article
			$t_article=_getArticle($t_BB['artCode']); 		// récup article
				$SPQty=$t_article['stockInit']; 			// init du stock initial : c'est le premier SPQty
				$stockMini=$t_article['artStockMini']; 		// init du stock mini
			$currentDate=""; 								// RAZ de la date pour être en situation de nouvelle date au test de la date
			echo "<br>DBG : art($currentArtCode) : New art ----------------------- stMini=$stockMini";
		} // FI New item
		
		if ($t_BB['besDate']!=$currentDate){ // nouvelle date, préparer l'accumulation des quantités
			$currentDate=$t_BB['besDate'];					// Date courante = nouvelle date
			echo "<br>DBG : art($currentArtCode), date($currentDate) : new date ---- ......";
		} // FI new date
		
		// comptabiliser le mouvement courant : besoin ou ressource
		$besOrig=$t_BB['besOrig'];
		switch ($besOrig) {	// selon que le type de ligne est un besoin, une ressource ou le résultat d'un calcul (4=previ)
			case 'BI' :	// Besoin Initial = Bidon pour déclencher le calcul des BN
				$BIQty=$BIQty+$t_BB['besQte']; break; 	// incrémenter les besoins initiaux (ou Bidon)
			case 'BB' :	// Besoin Brut = OV ou BF
				$BBQty=$BBQty+$t_BB['besQte']; break; 	// incrémenter les besoins
			case 'BP' :	// Besoins prévisionnels (OP transformés en BP)
				$BPQty=$BPQty+$t_BB['besQte']; break;	// incrémenter les besoins calculés (c'est une ressource prévisionnelle)
			case 'OL' :	// Ordre Lancé (OF ou OA)
				$OLQty=$OLQty+$t_BB['besQte']; break;	// incrémenter les ressources (fermes ou issues du calcul mrp)
			case 'OP' :	// Ordre Prévisionnel déjà calculé (achat ou fab, peu importe)
				$OPQty=$OPQty+$t_BB['besQte']; break;	// incrémenter les ressources (fermes ou issues du calcul mrp)
		}
		//echo "<br>DBG : Line : art($currentArtCode), date($currentDate) ".$t_BB['besOrig']." qty=".$t_BB['besQte']." ==>> BB=$BBQty, BP=$BPQty, OL=$OLQty, OP=$OPQty (besRef=".$t_BB['besRef'].")";
	} // Fin de la lecture de la requête
	if ($BIQty+$BBQty+$BPQty+$OLQty+$OPQty>0){ // Si il y a une quantité à écrire dans la ligne en cours
		// calcul des ordres prévisionnels (ce sont eux qui sont à planifier, pas le besoin net ...)
			$STQty=$SPQty;											// ST ------	reprise du stock de la ligne précédente (si même article ...)
			$BNQty=$stockMini+$BBQty+$BPQty-$OLQty-$OPQty-$STQty;	// BN ------ 	on ne tient pas compte des notions de lot ou de multiple ; 
			if ($BNQty<0) $BNQty=0;
			$newOPQty=$BNQty;										// OP ------	calcul des ordres prévisionnels ; pour l'instant pas de calcul, OP=BN
			$SPQty=$STQty+$OLQty+$OPQty-$BBQty-$BPQty+$newOPQty;	// SP ------ 	calcul du stock prévisionnel avec les besoins calculés pour la prochaine rotation ...
		
		// si un besoin existe alors écrire l'ordre prévisionnel à lancer dans le fichier mrpRessources
		//echo " newOP=$newOPQty (STMini=$stockMini ST=$STQty BB=$BBQty BP=$BPQty OL=$OLQty BN=$BNQty oldOP=$OPQty).";
		if ($newOPQty >0){
			$resRef=$t_BB['besOrig'].".".$level."-".$t_BB['artCode'];
			$besSQL= "INSERT INTO mrpressources (resOrig, artCode, resDate, resQte, resRef, resLevel) 
				VALUES ('OP', '$currentArtCode', '$currentDate', $newOPQty, '$resRef', $level);";
			DB_executerRequete($besSQL, $connexion);
			//echo "<br>*DBG* : ".mysql_error();
			//echo " >> line Written.";
			$insert=true;	// on a ajouté quelque chose
		}
	}
	return $insert;
}
//	_propagerNomenclatures() : On reprend les OP, on les transforme en besoins mrp (BP) via les nomenclatures et on change de niveau !!!
function _propagerNomenclatures($level) {
//	Donc une ressource peut devenir plusieurs besoins (un composé nécessite plusieurs composants)
global $connexion;
	$insert=false;	// on n'a rien ajouté dans les besoins

	$OPSQL="SELECT artCode, resDate, resQte, resRef FROM mrpressources WHERE resLevel=$level ;";
	$OPSQL_id=DB_executerRequete($OPSQL, $connexion);
	//echo "DBG : ".mysql_error();
	
	$besLevel=$level+1;
	while ($t_OP=DB_lireLigne($OPSQL_id)){
		//$t_art=_getArt($t_OP['artCode']); // récup de l'article pour savoir s'il est un composé d'une nomenclature
		$besRef="OP.".$level."-".$t_OP['artCode']."-".$t_OP['resRef'];
		$nomSQL="SELECT * FROM artnomenclature WHERE artCode = '".$t_OP['artCode']."' ;";
		$nomSQL_id=DB_executerRequete($nomSQL, $connexion);
		//if (DB_getNbEnreg($nomSQL_id)>0){
			while ($t_nom=DB_lireLigne($nomSQL_id)){
				$BPmrpQty=$t_OP['resQte']*$t_nom['compQte'];
				$BPSate=$t_OP['resDate'];
				$besNomSQL= "INSERT INTO mrpbesoins (besOrig, artCode, besDate, besQte, besRef, besType, besLevel) 
					VALUES ('BP', '".$t_nom['comp_artCode']."', '".$BPSate."', $BPmrpQty, '$besRef', 4, $besLevel);";
				DB_executerRequete($besNomSQL, $connexion);
				//echo "DBG : ".mysql_error();
				$insert=true;	// on a ajouté quelque chose
			}
		//}
	}
	return $insert;
}
// MRP_calculMRP() : Calcul MRP1 (pas MRP2 car pour l'instant on ne tiens pas compte des délais de fabrication, de livraison, l'ouverture des posete de travail, etc. ...)
function MRP_calculMRP() {
global $connexion;
	$errNum=0;	
// ==Passe 1 : Calcul des besoins de produits finis  ====================================
/* revoir l'algo : 
	passe initiale
	e0 vider les données inutiles = mrbbesoins et mrpressources
	e1 level0 : calculer les besoins brut pour le stock et importer les besoins brut fermes dans mrpbesoins
	e2 level0 : calculer les besoins nets (ressources théoriques) correspondant à la somme des besoins bruts, date par date à partir d'aujourd'hui
	e3 level0 : transformer ces ressources théoriques en besoins via les nomenclatures
	
	passe n°1
*/
echo "<div id=DBG style='background:white;'>";
// ==initialisations ------------------------------------------------------------------------------------
	// RAZ du calcul : vider la table des besoins 
	DB_executerRequete("DELETE FROM mrpbesoins ;", $connexion); // ou "TRUNCATE TABLE mrpbesoins ;"
	DB_executerRequete("DELETE FROM mrpressources ;", $connexion); // ou "TRUNCATE TABLE mrpressources ;"
	// importer les besoins et ressources fermes 
	$level=0;
	$iniBI	=_initStock($level);		// calcul d'un besoin bidon issu du stock de sécu afin de déclencher un vrai calcul des BN => BI Besoin Initial (ou BIdon)
	$iniBB	=_initBesoins($level);		// calcul des besoins bruts fermes				=> BB besoins
	$iniOL	=_initRessources($level);	// calcul des ordres lancés = ressources fermes	=> OL ressources
	
// ==Passe 1 : Calcul des besoins de produits finis et propagation via les nomenclatures ----------------
	$calcOP  =_calculBN($level);				// calcul des ordres planifiés  		=> OP ressources
	$calcNom =_propagerNomenclatures($level);	// calcul des besoins planifiés			=> BP besoins
	
// ==Passe 2 : Calcul des besoins des produits suivants avec propagation via les nomenclatures ----------
	//if ($iniBB || $iniOL || $calcOP || $calcNom){	
	if ($iniBI || $iniBB || $iniOL || $calcOP || $calcNom){
		if ($calcOP || $calcNom){
			$level++;
			$calcOP  =_calculBN($level);				// calcul des besoins nets
			$calcNom =_propagerNomenclatures($level);	// calcul des ordres planifiés
		}
	}
echo "</div id=DBG style='background:white;'>";
	return $errNum;
}
// --------------------------------------------------------------------------------------------------------------------
// ===================================================================================================
?>
