Commit a9dea9ef authored by thejoelinux's avatar thejoelinux

compta des souscriptions

parent b4509507
Implementation d'un système de rappel
Besoins fonctionnels :
A la base, il s'agit de faire des rappels concernant les emprunts
les rappels peuvent avoir des types différents.
type "rappel proche" - envoyer un mail de manière automatique, 48 heures avant la date de restitution du jeu, pour signaler à un utilisateur que ses emprunts arrivent à échéance. Il serait interressant de pouvoir proposer des jeux similaires - par exemple, une rubrique : "ce(s) jeu(x) vous ont plu ?" pour encourager les membres de l'association a non seulement venir pour rendre mais aussi pour emprunter de nouveaux titres.
type "rappel" - envoyer un mail plus formel pour signaler que le jeu est en retard. L'envoi de ces rappels ne se fait pas automatiquement (au moins dans un premier temps), c'est à l'initiative de la ludothécaire.
Les rappels se font de manière groupés, pour l'ensemble des jeux de l'utilisateurs.
Les textes servant de rappels sont stockés dans la base de données pour pouvoir être personnalisés par le gestionnaire.
Avant l'envoi, on propose une visualisation.
Le mail est envoyé avec une adresse de réponse valide qui permets aux personnes de répondre. L'adresse est également dans la base de données; si elle n'est pas valide (par exemple, quelque chose du genre "nepasrepondre@domain.tld") alors un texte est ajouté dans le mail pour le préciser.
Lors de la consultation des jeux "en retard" dans les emprunts, on peut voir s'ils sont déjà fait l'objet d'un ou plusieurs rappels.
Besoins techniques :
une table (avec controller et modèle) des "reminders"; une table qui fait le joint entre ces reminders et les emprunts concernés. Le texte généré au moment du mail est sauvegardé dans la base de données pour pouvoir s'y rapporter en cas de besoin.
Cas d'usage : la ludothécaire regarde la liste des emprunts en retard. Cette liste est triée selon les critères suivants : la date du dernier rappel (en premier, les plus anciens rappels, ou les jeux qui n'ont pas fait l'objet de rappels à part le rappel automatique)
Elle selectionne un utilisateur avec un certain nombre d'emprunts en retard.
alter table lud_membership_types add duration integer after price;
alter table lud_subscriptions drop column credit;
......@@ -158,11 +158,6 @@ class Member extends Record {
$subscription->update();
}
public function delete_subscription() {
$subscription = Subscription::fetch($GLOBALS["data"]->db_escape_string($_REQUEST["i"]));
$subscription->delete();
}
public function fetch_loans() {
Loan::fetch_all($this->loans, $this->id);
$this->loans_text = sizeof($this->loans) ?
......
<?php
class Membership_Type extends Record {
public $id, $name, $description, $price;
public $id, $name, $description, $duration, $price;
public static $table = "lud_membership_types";
......@@ -14,9 +14,9 @@ class Membership_Type extends Record {
public static function fetch_all(&$membership_types) {
$membership_types = array();
// FIXME : this request should include the number of current membership for
// every type. It could be an indication of wether you could delete it or not.
// every type. It could be an indication of whether you could delete it or not.
// SQL SELECT membership_types
$sql = "SELECT id, name, description, price
$sql = "SELECT id, name, description, price, duration
FROM ".Membership_Type::$table."
ORDER BY name";
$GLOBALS["data"]->select($sql, $membership_types, "Membership_Type", 1);
......@@ -25,7 +25,7 @@ class Membership_Type extends Record {
public static function fetch($id) {
// SQL SELECT membership_types
$sql = "SELECT id, name, description, price
$sql = "SELECT id, name, description, price, duration
FROM ".Membership_Type::$table."
WHERE id = ".$id;
$GLOBALS["data"]->select($sql, $membership_type, "Membership_Type");
......@@ -38,7 +38,8 @@ class Membership_Type extends Record {
$v->labels(array(
'name' => 'Le nom',
'description' => 'La description',
'price' => 'Le prix'
'price' => 'Le prix',
'duration' => 'La durée'
));
if($v->validate()) {
$errors = null;
......
......@@ -49,12 +49,12 @@ class Payment extends Record {
$comments, $amount) {
$payment = new Payment();
// SQL SELECT lud_payments lud_subscriptions
$sql = " SELECT s.price - sum(p.amount) AS amount_left
$sql = " SELECT s.price - sum(p.amount) AS amount_left, count(p.id) AS num_payment
FROM ".Subscription::$table." s
LEFT JOIN ".Payment::$table." p ON p.subscription_id = s.id
WHERE s.id = ".$subscription_id;
$GLOBALS["data"]->select($sql, $rset);
if($amount > $rset->value("amount_left")) {
if($rset->value("num_payment") > 0 && $amount > $rset->value("amount_left")) {
$payment->error = "La somme des paiements est supérieure au prix de l'adhésion";
return $payment;
}
......
......@@ -2,7 +2,7 @@
class Subscription extends Record {
public $id, $start_date, $end_date, $member_id, $member_name, $membership_type_id, $payment_method_id;
public $price, $credit, $comments, $created_at, $updated_at;
public $price, $comments, $created_at, $updated_at;
public static $table = "lud_subscriptions";
......@@ -25,7 +25,7 @@ class Subscription extends Record {
$sql = " SELECT ms.id, start_date, end_date, ms.member_id, CONCAT(m.lastname, ' ', m.firstname) as member_name,
ms.membership_type_id, mt.name as membership_type_name,
COUNT(p.id) AS num_payment, sum(p.amount) AS total_payment,
ms.price, credit, ms.comments, ms.created_at, ms.updated_at,
ms.price, ms.comments, ms.created_at, ms.updated_at,
DATEDIFF(end_date, curdate()) as remaining_days
FROM ".Subscription::$table." ms, ".Member::$table." m, ".Membership_Type::$table." mt, ".Payment::$table." p
WHERE ms.id = ".$id."
......@@ -48,7 +48,7 @@ class Subscription extends Record {
$sql = " SELECT ms.id, start_date, end_date, ms.member_id, CONCAT(m.lastname, ' ', m.firstname) as member_name,
ms.membership_type_id, mt.name as membership_type_name,
COUNT(p.id) AS num_payment, sum(p.amount) AS total_payment,
ms.price, credit, ms.comments, ms.created_at, ms.updated_at,
ms.price, ms.comments, ms.created_at, ms.updated_at,
DATEDIFF(end_date, curdate()) as remaining_days
FROM ".Subscription::$table." ms
LEFT JOIN ".Member::$table." m ON (ms.member_id = m.id)
......
......@@ -229,7 +229,6 @@ class MembersController extends AppController {
" - ".$member->firstname." ".$member->lastname);
$member->save_account();
}
$_REQUEST["credit"] = (array_key_exists("credit", $_REQUEST) && $_REQUEST["credit"] == "on") ? 1 : 0;
$member->create_subscription();
Account::ludotheque_new_subscription("Adhésion #".$member->id.
" - ".$member->firstname." ".$member->lastname,
......@@ -246,13 +245,15 @@ class MembersController extends AppController {
}
function _update_subscription() {
// you can't change the type of a subscription, only the dates
// and the comment
// So you got to delete and create a new one
$_REQUEST["a"] = "subscriptions";
$this->context["request"] = $_REQUEST;
try {
$member = Member::fetch($GLOBALS["data"]->db_escape_string($_REQUEST["i"]));
if($member->id != 0) {
$_REQUEST["member_id"] = $member->id;
$_REQUEST["credit"] = ($_REQUEST["credit"] == "on") ? 1 : 0;
$member->update_subscription();
$member->fetch_subscriptions();
$this->set("member", $member);
......@@ -262,26 +263,7 @@ class MembersController extends AppController {
}
} catch(data_exception $e) {
return "data_exception";
}
}
function _delete_subscription() {
$_REQUEST["a"] = "subscriptions";
$this->context["request"] = $_REQUEST;
try {
$member = Member::fetch($GLOBALS["data"]->db_escape_string($_REQUEST["member_id"]));
if($member->id != 0) {
$member->delete_subscription();
$member->fetch_subscriptions();
$_REQUEST["i"] = $member->id;
$this->set("member", $member);
return "members/subscriptions";
} else {
return "members/not_found"; // TODO
}
} catch(data_exception $e) {
return "data_exception";
}
}
}
function _list() {
......
......@@ -50,6 +50,42 @@ class SubscriptionsController extends AppController {
}
}
// API call from the list or the modal
function _delete() {
// $this->context["request"] = $_REQUEST;
try {
$member = Member::fetch($GLOBALS["data"]->db_escape_string($_REQUEST["member_id"]));
if($member->id != 0) {
if(!$member->account_id) {
$member->account_id = Account::create_for_member("Adhérent #".$member->id.
" - ".$member->firstname." ".$member->lastname);
$member->save_account();
}
// check the subscription you want to delete
// you can't delete a subscription if there are payments on it
$subscription = Subscription::fetch($GLOBALS["data"]->db_escape_string($_REQUEST["i"]));
if($subscription->num_payment != 0) {
// do not FIXME : tell that to the user
AppController::http_error(422);
$subscription->error = "Impossible de supprimer une adhésion avec des payments associés";
} else {
$subscription->delete();
Account::ludotheque_new_subscription("Annulation adhésion #".$member->id.
" - ".$member->firstname." ".$member->lastname,
date("Y-m-d"), $member->account_id, 0 - $subscription->price);
}
echo json_encode($subscription);
exit();
} else {
AppController::http_error(404);
exit();
}
} catch(data_exception $e) {
AppController::http_error(500);
exit();
}
}
function _print() {
try {
$subscription = Subscription::fetch($GLOBALS["data"]->db_escape_string($_REQUEST["i"]));
......
......@@ -26,7 +26,6 @@
<th>Type</th>
<th>Prix</th>
<th>Paiement</th>
<th>Crédit</th>
<th>Notes</th>
<th>Actions</th>
</tr>
......@@ -39,7 +38,6 @@
<td>{{ val.membership_type_name }}</td>
<td>{{ val.price }}</td>
<td>{{ val.total_payment }}</td>
<td>{{ val.credit ? "Oui" : "Non" }}</td>
<td>{{ val.comments }}</td>
<td>
<button type="button" class="btn btn-success btn-sm"
......@@ -53,9 +51,11 @@
<button type="button" class="btn btn-default btn-sm button_print" data-id="{{ val.id }}">
<i class="glyphicon glyphicon-print"></i>
</button>
{% if val.num_payment == 0 %}
<button type="button" class="btn btn-danger btn-sm button_delete" data-id="{{ val.id }}">
<i class="glyphicon glyphicon-trash"></i>
</button>
{% endif %}
</td>
{% else %}
<tr>
......@@ -112,7 +112,7 @@ $(document).ready(function () {
if(confirm('Êtes vous sur ?')) {
var button = $(e.currentTarget);
$.post('api.php', { o: 'subscriptions', a: 'delete',
i: button.data('id') })
i: button.data('id'), member_id: $('#member_id').val() })
.done(function(data) {
$('#subs_' + data.id).remove();
})
......
......@@ -15,11 +15,16 @@
<input type="text" id="name" name="name" class="form-control" value="{{ object.name }}"/>
<span id="help-name" class="help-block" style="display: none"></span>
</div>
<label class="control-label col-sm-2" for="price">Prix</label>
<div class="col-sm-4">
<label class="control-label col-sm-1" for="price">Prix</label>
<div class="col-sm-2">
<input type="text" id="price" name="price" class="form-control" value="{{ object.price }}"/>
<span id="help-price" class="help-block" style="display: none"></span>
</div>
<label class="control-label col-sm-1" for="duration">Durée</label>
<div class="col-sm-2">
<input type="text" id="duration" name="duration" class="form-control" value="{{ object.duration }}"/>
<span id="help-duration" class="help-block" style="display: none"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="description">Description</label>
......@@ -29,8 +34,11 @@
</div>
</div>
{% endblock %}
{% block return_url %}
var return_url = 'index.php?o=membership_types&a=list';
{% endblock %}
{% block javascript_fields %}
var fields = ['name', 'description', 'price'];
var fields = ['name', 'description', 'price', 'duration'];
{% endblock %}
{% block javascript_msg %}
var msg = 'Voulez-vous réellement supprimer ce type d\'adhésion ?\n' +
......
......@@ -27,6 +27,7 @@
<th>Nom</th>
<th>Description</th>
<th>Tarif</th>
<th>Durée (jours)</th>
<th>Actions</th>
</tr>
</thead>
......@@ -36,6 +37,7 @@
<th>{{ val.name }}</th>
<td>{{ val.description }}</td>
<td>{{ val.price }}</td>
<td>{{ val.duration }}</td>
<td align="center">
<button type="button" class="btn btn-success btn-xs"
data-toggle="modal" data-target="#editModal" data-id="{{ val.id }}">
......
......@@ -9,6 +9,7 @@
<div class="modal-body">
{% block modal_body %}{% endblock %}
</div>
{% block modal_footer %}
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
{% if object.id != 0 %}
......@@ -20,6 +21,7 @@
<input type="button" class="btn btn-success" id="save_button" value="Créer">
{% endif %}
</div>
{% endblock %}
</div>
<script>
......@@ -46,7 +48,7 @@ $('#save_button').click(function(){
.done(function( data ) {
$('#created_ok').show();
if(return_url != '') {
window.setTimeout(1000,
window.setTimeout(1000,
location.href = return_url);
}
})
......
......@@ -54,19 +54,29 @@
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="membership_type_id">Type d'adhésion</label>
<label class="control-label col-sm-2" for="membership_type_id">Type</label>
<div class="col-sm-4">
<select id="membership_type_id" name="membership_type_id" class="form-control">
{% if object.id == 0 %}
<select id="membership_type_id" name="membership_type_id"
onChange="update_dependant_field()" class="form-control">
</select>
<script>
$('#membership_type_id').html('<option value="">Loading...</option>');
var duration_array = [];
var price_array = [];
$.ajax({url: 'api.php?o=membership_types&a=list',
success: function(output) {
var html = '';
$.each(output, function(key, val){
html = html + '<option value="' + val.id + '"'
+ (val.id == {{ object.membership_type_id ?: 0 }} ? ' selected ' : '' ) + '>'
+ val.name + '</option>';
duration_array[val.id] = val.duration;
price_array[val.id] = val.price;
html = html + '<option value="' + val.id + '"';
if(val.id == {{ object.membership_type_id ?: 0 }}) {
html = html + ' selected ';
$('#price').val(val.price);
// FIXME add duration to date in end_date
}
html = html + '>' + val.name + '</option>';
});
$('#membership_type_id').html(html);
},
......@@ -75,21 +85,27 @@
$('#membership_type_id').html('<option value="">' + xhr.status + ' ' + thrownError + '</option>');
// alert(xhr.status + " " + thrownError);
}});
function update_dependant_field() {
$('#price').val(price_array[$('#membership_type_id').val()]);
}
</script>
{% else %}
{{ object.membership_type_name }}
{% endif %}
</div>
<label class="control-label col-sm-2" for="payment_method_id">Paiements</label>
<div class="col-sm-4">
FIXME : link
{{ object.num_payment }} paiements effectué(s)
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="price">Prix</label>
<div class="col-sm-4">
{% if object.id == 0 %}
<input type="text" id="price" name="price" class="form-control" value="{{ object.price }}"/>
</div>
<label class="control-label col-sm-2" for="credit">Crédit</label>
<div class="col-sm-1">
<input type="checkbox" id="credit" name="credit" class="form-control" {{ object.credit ? "checked" }}/>
{% else %}
{{ object.price | number_format(2, '.', ' ') }} €
{% endif %}
</div>
</div>
<div class="form-group">
......@@ -105,7 +121,9 @@
{% if object.id %}
<input type="hidden" name="subscription_id" id="subscription_id" value="{{ object.id }}">
<input type="button" class="btn btn-success" id="save_button" value="Enregistrer les changements">
<input type="button" class="btn btn-danger" id="delete_button" value="Supprimer">
{% if object.num_payment == 0 %}
<input type="button" class="btn btn-danger" id="delete_button" data-id="{{ object.id }}" value="Supprimer">
{% endif %}
{% else %}
<input type="hidden" name="subscription_id" id="subscription_id" value="0">
<input type="button" class="btn btn-success" id="save_button" value="Créer">
......@@ -132,12 +150,21 @@ $(document).ready(function () {
document.defaultform.submit();
return true;
});
$('#delete_button').click(function(){
$('#delete_button').click(function(e){
var msg = 'Voulez-vous réellement supprimer une adhésion ?\n' +
'Cette action n\'est possible qu\'en cas d\'erreur de saisie.\n';
if(confirm(msg)) {
$('#a').val('delete_subscription');
document.defaultform.submit();
var button = $(e.currentTarget);
$.post('api.php', { o: 'subscriptions', a: 'delete',
i: button.data('id') })
.done(function(data) {
alert('Supression effectuée');
location.href = "index.php?o=subscriptions&a=list&i={{ object.member_id }}";
})
.fail(function() {
alert('Erreur lors de la suppression de l\'adhésion');
});
return;
}
});
});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment