bouffeatulm/rbmt_admin.php

489 lines
17 KiB
PHP

<?php
require('include.php');
init(true, true);
//Return an array with date of the start of the month and of the end of the month
function bornes_mois($num_mois,$annee)
{
$debut_mois = mktime(0, 0, 0,$num_mois, 1, $annee);
$dernier_jour = array(
1=>31,
2=>28+date('L'),
3=>31,
4=>30,
5=>31,
6=>30,
7=>31,
8=>31,
9=>30,
10=>31,
11=>30,
12=>31);
$fin_mois = mktime(23, 59, 59, $num_mois, $dernier_jour[$num_mois], $annee);
$bornes = array($debut_mois, $fin_mois);
return $bornes;
}
//Return what A must pay to B
function dettes($A, $B, $debut_mois, $fin_mois)
{
global $bdd;
//When A paid and B was here
$reqA_B = $bdd->prepare('SELECT id, date, montant, copains, invites FROM depenses WHERE date>'.$debut_mois.' AND date<'.$fin_mois.' AND de=:param1 AND (copains LIKE "%,'.(int) $B.',%" OR copains LIKE "%,'.(int)$B.'" OR copains LIKE "'.(int) $B.',%" OR copains LIKE "'.(int) $B.'")');
$reqA_B->bindvalue(':param1',$A);
$reqA_B->execute();
//When B paid and A was here
$reqB_A = $bdd->prepare('SELECT id, date, montant, copains, invites FROM depenses WHERE date>'.$debut_mois.' AND date<'.$fin_mois.' AND de=:param1 AND (copains LIKE "%,'.(int) $A.',%" OR copains LIKE "%,'.(int)$A.'" OR copains LIKE "'.(int) $A.',%" OR copains LIKE "'.(int) $A.'")');
$reqB_A->bindvalue(':param1',$B);
$reqB_A->execute();
//What A already paid to B
$reqPaiementsA_B = $bdd -> prepare('SELECT paiements.montant AS montant FROM paiements LEFT JOIN depenses ON paiements.id_depense=depenses.id WHERE depenses.date >'.$debut_mois.' AND depenses.date<'.$fin_mois.' AND paiements.de=:de AND paiements.a=:a');
$reqPaiementsA_B->bindvalue(':de',$A);
$reqPaiementsA_B->bindvalue(':a',$B);
$reqPaiementsA_B->execute();
//Same thing for B to A
$reqPaiementsB_A = $bdd -> prepare('SELECT paiements.montant AS montant FROM paiements LEFT JOIN depenses ON paiements.id_depense=depenses.id WHERE depenses.date >'.$debut_mois.' AND depenses.date<'.$fin_mois.' AND paiements.de=:de AND paiements.a=:a');
$reqPaiementsB_A->bindvalue(':de',$B);
$reqPaiementsB_A->bindvalue(':a',$A);
$reqPaiementsB_A->execute();
//$deltaAB : What A must pay to B
$deltaAB = 0;
while($donneesA_B = $reqA_B->fetch())
{
//We get the price of the meal, divided by the number of people who ate
//nb of comma + 1
$deltaAB -= $donneesA_B['montant']/(substr_count($donneesA_B['copains'], ',') + 1 + $donneesA_B['invites']);
}
while($donneesB_A = $reqB_A->fetch()) //idem
{
$deltaAB += $donneesB_A['montant']/(substr_count($donneesB_A['copains'], ',') + 1 + $donneesB_A['invites']);
}
while($donneesPaiementsA_B = $reqPaiementsA_B->fetch()) //what has been already paid
{
$deltaAB -= $donneesPaiementsA_B['montant'];
}
while($donneesPaiementsB_A = $reqPaiementsB_A->fetch()) //idem
{
$deltaAB += $donneesPaiementsB_A['montant'];
}
return $deltaAB;
}
// Reduces the matrix in n steps
function simplify ($copains){
// We work directly with $debts
global $debts;
global $matrix;
$max = maximum($debts);
// On cherche le plus proche en valeur absolue du maximum
$near = nearest($debts, $max);
// On en déduit l'écart entre leurs valeurs absolues
$delta = $max["val"] + $near["val"];
// Case :
//A : max 100
//B :near -90
// delta 10
// m[A][B] -90
// m[B][A] 90
if ($delta > 0){
$matrix[$max["id"]][$near["id"]] = -$debts[$near["id"]];
$matrix[$near["id"]][$max["id"]] = $debts[$near["id"]];
$debts[$max["id"]] = $delta;
$debts[$near["id"]] = 0;
}
else {
$matrix[$max["id"]][$near["id"]] = $debts[$max["id"]];
$matrix[$near["id"]][$max["id"]] = -$debts[$max["id"]];
$debts[$max["id"]] = 0;
$debts[$near["id"]] = $delta;
}
return $matrix;
}
// Return the nearest value
function nearest($debts, $max)
{
// Initialize with the maximum distance
$d_min = max($debts) - min($debts);
// Initialize the default id to one key in the array
$debts_keys = array_keys($debts);
$id_min = $debts_keys[1];
foreach($debts as $id => $val)
{
// Computes the absolute distance
$d = abs($max["val"] + $val);
// If it's a new minimum AND the value isn't positive
if ($d <= $d_min && $val < 0)
{
$id_min = $id;
$d_min = $d;
}
}
return array("id"=>$id_min, "val"=>$debts[$id_min]);
}
function maximum($tableau)
{
$max["id"] = 0;
$max["val"] = 0;
foreach($tableau as $id=>$val)
{
if($val >= $max["val"])
{
$max["val"] = $val;
$max["id"] = $id;
}
}
return $max;
}
// Function to insert a regulation between A and B for the simplification
// system only.
function inserer_paiement_rbmt($donnees_depense, $de_paiement, $a_paiement, $rbmt)
{
if ($de_paiement == $a_paiement) return 1;
global $bdd;
// We get all the payments between a and de (we just filter thanks to the
// id which is linked to the a field)
$paiement_existe_req = $bdd->prepare('SELECT montant, COUNT(*) AS nbre_paiement FROM paiements WHERE id_depense=:id_depense AND de=:de');
$paiement_existe_req->bindValue(':id_depense', $donnees_depense['id']);
$paiement_existe_req->bindValue(':de', $de_paiement);
$paiement_existe_req->execute();
$deja_paye = 0;
while ($donnees = $paiement_existe_req->fetch())
{
$deja_paye += $donnees['montant'];
}
$montant = $donnees_depense['montant']/(substr_count($donnees_depense['copains'], ',') + 1 + $donnees_depense['invites']) - $deja_paye;
$req = $bdd->prepare('INSERT INTO paiements(id, de, a, id_depense, date, montant, rbmt) VALUES("", :de, :a, :id_depense, :date, :montant, :rbmt)');
$req->bindValue(':de', $de_paiement);
$req->bindValue(':a', $a_paiement);
$req->bindValue(':id_depense', $donnees_depense['id']);
$req->bindValue(':date', time());
$req->bindValue(':rbmt', $rbmt);
$req->bindValue(':montant', $montant);
$req->execute();
return 1;
}
if(!(isset($_GET['confirm']) && isset($_POST['matrix']) && isset($_POST['copains']) && isset($_POST['date'])))
{
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Bouffe@Ulm : remboursements</title>
<link rel="stylesheet" media="screen" type="text/css" href="misc/design.css" />
<link rel="icon" href="favicon.ico" />
</head>
<body>
<h1>Remboursements simplifiés</h1>
<?php
}
// Creation Step 1
// show a form to get more info
if(isset($_GET["action"]) && $_GET["action"] == "new")
{
$req = $bdd->query('SELECT id, nom FROM copains ORDER BY nom ASC');
while($copain = $req->fetch())
{
$copains[$copain['id']] = $copain['nom']; //And put it in an array
}
?>
<form method="post" action="rbmt_admin.php?">
<p><label for="jour">Date : </label>
<select name="jour" id="jour">
<?php
for($i=1; $i<32; $i++)
{
if((date('j') == $i && !isset($modif)) || (isset($modif) && date('j', $donnees['date']) == $i))
echo "<option value='".$i."' selected='selected'>".$i."</option>";
else
echo "<option value='".$i."'>".$i."</option>";
}
?>
</select>
<select name="mois" id="mois">
<?php
for($i=1; $i<13; $i++)
{
if((date('m') == $i && !isset($modif)) || (isset($modif) && date('m', $donnees['date']) == $i))
echo "<option value='".$i."' selected='selected'>".$i."</option>";
else
echo "<option value='".$i."'>".$i."</option>";
}
?>
</select>
<select name="AM_PM">
<option value='11' <?php if((date('A') == "AM" && !isset($modif)) || (isset($modif) && date('A', $donnees['date']) == "AM")) { echo 'selected="selected"';}?>>Midi</option>
<option value='22' <?php if((date('A') == "PM" && !isset($modif)) || (isset($modif) && date('A', $donnees['date']) == "PM")) { echo 'selected="selected"';}?>>Soir</option>
</select>
<select name="annee" id="annee">
<?php
for($i=date('Y')-1; $i<date('Y')+2; $i++)
{
if((date('Y') == $i && !isset($modif)) || (isset($modif) && date('Y', $donnees['date']) == $i))
echo "<option value='".$i."' selected='selected'>".$i."</option>";
else
echo "<option value='".$i."'>".$i."</option>";
}
?>
</select>
</p>
<p style="text-align: left; display: inline-block;">Copains : <br/>
<?php
$req2 = $bdd->query('SELECT id, nom FROM copains ORDER BY nom ASC');
while($donnees2 = $req2->fetch())
{
echo "<input type='checkbox' name='copain_".htmlspecialchars($donnees2['id'])."' id='copain_".htmlspecialchars($donnees2['id'])."' checked='checked'/><label for='copain_".htmlspecialchars($donnees2['id'])."' class='inline'>".htmlspecialchars($donnees2['nom'])."</label><br/>";
}
?>
</p>
<p>
<input type="submit" value="<?php if(isset($modif)) { echo 'Modifier'; } else { echo 'Ajouter';}?>"/> ou <a href="index.php">retour à l'accueil</a><input type="hidden" name="id" value="<?php if(isset($modif)) { echo $modif;}?>"/>
<input type="hidden" name="token" value="<?php echo $_SESSION['token_modif'];?>"/>
</p>
</form>
<?php
}
// Creation Step 2
// check there's everything, create the new simplified matrix and prompt
// for confirmation
else if (isset($_POST['jour']) && isset($_POST['mois']) && isset($_POST['annee']) && isset($_POST['AM_PM']))
{
$req = $bdd->prepare('SELECT nom, id FROM copains ORDER BY nom ASC');
$req->execute();
// Fill an array only with friends who are participating
while($copain = $req->fetch())
{
// If the friend was selected
if(isset($_POST['copain_'.$copain['id']]))
$copains[$copain['id']] = $copain['nom'];
}
$n = count($copains); // Usefull for the size of the array
// Create the temporal bounds for the requests
$debut_mois = 0;
$fin_mois = mktime($_POST['AM_PM'], 0, 0, $_POST['mois'], $_POST['jour'], $_POST['annee']);
// We create an array containing the total debts and a matrix
foreach($copains as $idA=>$nameA)
{
$debts[$idA] = 0;
foreach($copains as $idB=>$nameB)
{
$matrix[$idA][$idB] = 0;
}
}
// We initialize the debts array
foreach($copains as $keyA=>$copainA)
{
foreach($copains as $keyB=>$copainB)
{
$dette = dettes($keyA,$keyB, $debut_mois, $fin_mois);
$deltaAB = $dette;
$debts[$keyA] += $deltaAB;
}
}
// To avoid an infinite while loop, we have to round the value
foreach($debts as &$val)
$val = round($val, 2);
// Should be zero, but with float error, it may be non null
$error = array_sum($debts);
// Do it in n steps
for ($i=0; $i<$n; $i++)
$matrix = simplify($copains);
echo "<h2>Récapitulatif</h2><p>";
// Output the matrix
foreach($copains as $keyA=>$copainA)
{
foreach($copains as $keyB=>$copainB)
{
if (isset($matrix[$keyA][$keyB]) && $matrix[$keyA][$keyB] > 0)
echo '<b>' . $copains[$keyA] . '</b> doit '. round($matrix[$keyA][$keyB], 2) . ' à <b>' . $copains[$keyB] .'</b><br/>';
}
}
// Show a forme to confirm
?>
</p>
<form method="post" action="rbmt_admin.php?confirm=1">
<p>
<input type="hidden" name="date" value='<?php echo $fin_mois;?>' />
<input type="hidden" name="matrix" value='<?php echo serialize($matrix); ?>'/>
<input type="hidden" name="copains" value='<?php echo serialize($copains); ?>'/>
<input type="submit" value="Confirmer"/> ou <a href="index.php">Retour à l'accueil</a><br/>
<em>Attention, cette opération est irréversible et annulera tous les paiements jusqu'à la date choisie.</em>
</p>
</form>
<?php
}
// Creation Step 3
// let's include some stuff
else if (isset($_GET['confirm']) && isset($_POST['matrix']) && isset($_POST['copains']) && isset($_POST['date']))
{
$req = $bdd->prepare('INSERT INTO remboursements(date, matrix, id, copains) VALUES(:date, :matrix, "", :copains)');
$req->bindValue(':date', $_POST['date']);
$req->bindValue(':matrix', $_POST['matrix']);
$req->bindValue(':copains', $_POST['copains']);
// We insert the new matrix in the table
if (!$req->execute())
echo "Une erreur est survenue";
else
{
// We get the biggest id (here, the one we just created)
$req = $bdd->query('SELECT MAX(id) FROM remboursements');
$retour = $req->fetch();
$id = $retour["MAX(id)"];
// Now we can simplify the matrix by inserting as many regulation as
// necessary
$copains = unserialize($_POST['copains']);
foreach($copains as $a=>$nomA)
{
foreach($copains as $de=>$nomB)
{
$req = $bdd->prepare('SELECT id, de, copains, montant, invites FROM depenses WHERE (copains LIKE "%,'.$de.',%" OR copains LIKE "%,'.$de.'" OR copains LIKE "'.$de.',%" OR copains LIKE "'.$de.'") AND de=:a AND date>:debut_mois AND date<:fin_mois');
$req->bindValue(':a', $a);
// Bounds are 0 and the date given in the previous form
$req->bindValue(':debut_mois', 0);
$req->bindValue(':fin_mois', $_POST['date']);
$req->execute();
// We've got a bunch of exchange to insert (with a flag to specify we
// added it here)
while($donnees = $req->fetch())
{
if(!empty($de))
{
inserer_paiement_rbmt($donnees, $de, $a, $id);
}
else
{
//For all the people who participate...
$participants = explode(',', $donnees['copains']);
foreach($participants as $participant)
{
// If we've got someone which participates to the
// simplification
if (in_array($participant, array_keys($copains)))
inserer_paiement_rbmt($donnees, $participant, $a, $id);
}
}
}
}
}
// Everything went right
header('location: message.php?id=14');
}
}
// If we want to delete one and have the id
else if(isset($_GET['id']) && isset($_GET['action']) && $_GET['action'] == "del")
{
// If already confirmed
if(isset($_POST['confirm']))
{
// We delete the simplification AND the regulation it created
if($bdd->query('DELETE FROM remboursements WHERE id='.$_GET['id']) &&
$bdd->query('DELETE FROM paiements WHERE rbmt='.$_GET['id']))
header('location: message.php?id=15');
else
header('location: message.php?id=16');
}
else
{
$req = $bdd->prepare('SELECT * FROM remboursements WHERE id='.$_GET['id']);
?>
<form method="post" action="rbmt_admin.php?<?php echo $_SERVER['QUERY_STRING'];?>">
<p>
<input type="submit" value="Confirmer" /> ou <a href="index.php">Retour à l'accueil</a>
<input type="hidden" name="confirm" value="1"/>
</p>
</form>
<?php
}
}
// Else, we print the list
else
{
echo '<a href="?action=new">Nouveau remboursement</a> | <a href="index.php">Retour à l\'accueil</a>';
echo '<h2>Remboursements précédents</h2>';
$req = $bdd->prepare('SELECT * FROM remboursements ');
$req->execute();
$table = '';
$links = '';
while($data = $req->fetch())
{
// Reset the list of friend to ''
$liste = '';
// Extract info from $data
// First the date
$date = date('j/m', $data['date']);
// List of friend
$copains = unserialize($data['copains']);
foreach($copains as $nom)
$liste .= ', ' . $nom;
// If we clicked on one particular remboursement
if (isset($_GET['id']) && $_GET['id'] == $data['id'] && isset($_GET['action']) && $_GET['action'] == "show")
{
$table .= "<li>Le {$date}{$liste} - <a href='?id={$data['id']}&amp;action=del'>Supprimer</a>";
$matrix = unserialize($data['matrix']);
// We build an array containing the data we want to print
// and a list containing the other remboursement
$table.='
<table>
<tr>
<th class="centre">Doit\À</th>';
//Construct the header of the table and display it for the previous months
foreach($copains as $key => $copain)
{
if($_SESSION['nom'] == $copain)
$copain = '<strong>'.$copain.'</strong>';
$table .= '<th class="centre">'.$copain.'</th>';
}
$table .= '</tr>';
foreach($copains as $keyA=>$copainA)
{
if($_SESSION['nom'] == $copainA)
$copainA = '<strong>'.$copains[$keyA].'</strong>';
$table .= '<tr><th class="centre">'.$copainA.'</th>';
foreach($copains as $keyB=>$copainB)
{
if($matrix[$keyA][$keyB] <= 0)
$table .= '<td class="centre">-</td>';
else
$table .= '<td class="centre">'.$matrix[$keyA][$keyB].'</td>';
}
$table .= '</tr>';
}
$table .= '</table></li>';
}
// Else, we print a link
else
$links .= "<li><a href='?id={$data['id']}&amp;action=show'>Le {$date}{$liste}</a> - <a href='?id={$data['id']}&amp;action=del'>Supprimer</a></li>";
}
}
echo '<p><ul>';
if (isset($table)) echo $table;
if (isset($links)) echo $links;
echo '</ul></p>';
echo '<p>';
echo '<a href="index.php">Retour à l\'accueil</a>';
echo '</p>';
echo '</body></html>';
?>