Commit a2b18add by Félicie

Merge branch 'dev_cooperatic' into 2221-info-text

parents cc58df99 e6577d4f
...@@ -43,13 +43,32 @@ class CagetteEnvelops(models.Model): ...@@ -43,13 +43,32 @@ class CagetteEnvelops(models.Model):
try: try:
# Get invoice # Get invoice
cond = [['partner_id', '=', data['partner_id']]]
# Get specific invoice if id is given
if 'invoice_id' in data:
cond = [['id', '=', data['invoice_id']], ["number", "!=", False]]
else:
cond = [['partner_id', '=', data['partner_id']], ["number", "!=", False]]
fields = ['id', 'name', 'number', 'partner_id', 'residual_signed'] fields = ['id', 'name', 'number', 'partner_id', 'residual_signed']
invoice_res = self.o_api.search_read('account.invoice', cond, fields) invoice_res = self.o_api.search_read('account.invoice', cond, fields)
# Check if invoice exists # Check if invoice exists
if len(invoice_res) > 0: if len(invoice_res) > 0:
invoice = invoice_res[0] invoice = None
# Get first invoice for which amount being paid <= amount left to pay in invoice
for invoice_item in invoice_res:
if int(float(data['amount']) * 100) <= int(float(invoice_item['residual_signed']) * 100):
invoice = invoice_ite
if invoice is None:
res['error'] = 'The amount is too high for the invoices found for this partner.'
try:
# Got an error while logging...
coop_logger.error(res['error'] + ' : %s', str(data))
except Exception as e:
print(str(e))
return res
else: else:
res['error'] = 'No invoice found for this partner, can\'t create payment.' res['error'] = 'No invoice found for this partner, can\'t create payment.'
coop_logger.error(res['error'] + ' : %s', str(data)) coop_logger.error(res['error'] + ' : %s', str(data))
...@@ -79,6 +98,7 @@ class CagetteEnvelops(models.Model): ...@@ -79,6 +98,7 @@ class CagetteEnvelops(models.Model):
except Exception as e: except Exception as e:
res['error'] = repr(e) res['error'] = repr(e)
coop_logger.error(res['error'] + ' : %s', str(args)) coop_logger.error(res['error'] + ' : %s', str(args))
# Exception rises when odoo method returns nothing # Exception rises when odoo method returns nothing
marshal_none_error = 'cannot marshal None unless allow_none is enabled' marshal_none_error = 'cannot marshal None unless allow_none is enabled'
try: try:
...@@ -102,7 +122,6 @@ class CagetteEnvelops(models.Model): ...@@ -102,7 +122,6 @@ class CagetteEnvelops(models.Model):
coop_logger.error(res['error'] + ' : %s', str(data)) coop_logger.error(res['error'] + ' : %s', str(data))
if not ('error' in res): if not ('error' in res):
res['done'] = True res['done'] = True
res['payment_id'] = payment_id res['payment_id'] = payment_id
......
...@@ -2,6 +2,12 @@ ...@@ -2,6 +2,12 @@
margin-top: 15px; margin-top: 15px;
} }
#admin_connexion_button {
position: absolute;
top: 5px;
right: 5px;
}
.envelop_section { .envelop_section {
margin-bottom: 10px; margin-bottom: 10px;
} }
...@@ -10,16 +16,21 @@ ...@@ -10,16 +16,21 @@
display: none; display: none;
cursor: pointer; cursor: pointer;
margin-bottom: 15px; margin-bottom: 15px;
width: 25%;
} }
#cash_envelops, #ch_envelops, #archive_cash_envelops, #archive_ch_envelops { #cash_envelops, #ch_envelops, #archive_cash_envelops, #archive_ch_envelops {
margin-top: 30px; margin-top: 30px;
} }
.update_envelop_button, .delete_envelop_button, .envelop_comment { .delete_envelop_button, .envelop_comment {
margin: 0 0 15px 15px; margin: 0 0 15px 15px;
} }
.update_envelop_button, .add_to_envelop_button {
margin: 0 0 15px 10px;
}
.envelop_content_list { .envelop_content_list {
margin: 20px 0 15px 0; margin: 20px 0 15px 0;
} }
...@@ -53,18 +64,18 @@ ...@@ -53,18 +64,18 @@
flex: 50% 1 0; flex: 50% 1 0;
} }
.line_partner_name_container { .update_envelop_line .line_partner_name_container {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
} }
.line_partner_name { .update_envelop_line .line_partner_name {
text-align: left; text-align: left;
padding: 0 5px; padding: 0 5px;
} }
.line_partner_input_container { .update_envelop_line .line_partner_input_container {
display: flex; display: flex;
align-items: center; align-items: center;
} }
...@@ -80,6 +91,59 @@ ...@@ -80,6 +91,59 @@
margin-bottom: 20px; margin-bottom: 20px;
} }
/* Add payments to envelop modal */
.search_member_area {
margin: 20px 0;
}
.search_member_results_area {
margin: 20px 0;
}
.add_to_envelop_lines_area {
margin: 20px 0;
}
.add_to_envelop_lines {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 10px 0;
}
.add_to_envelop_line {
display: flex;
justify-content: center;
margin: 10px 0;
}
.add_to_envelop_line .partner_name_container {
display: flex;
justify-content: flex-start;
align-items: center;
}
.add_to_envelop_line .partner_input_container {
display: flex;
flex-direction: column;
align-items: flex-start;
margin-left: 10px;
width: 300px;
}
.line_partner_amount_error {
display: none;
color: #d9534f;
font-style: italic;
}
.confirm_add_payment_details {
font-weight: bold;
margin: 0 3px;
}
/* Accordion style */ /* Accordion style */
/* Style the buttons that are used to open and close the accordion panel */ /* Style the buttons that are used to open and close the accordion panel */
.accordion { .accordion {
......
...@@ -3,6 +3,8 @@ var archive_cash_envelops = []; ...@@ -3,6 +3,8 @@ var archive_cash_envelops = [];
var ch_envelops = []; var ch_envelops = [];
var archive_ch_envelops = []; var archive_ch_envelops = [];
var envelop_to_update = null; var envelop_to_update = null;
var members_search_results = [];
var selected_member = null;
function reset() { function reset() {
$('#cash_envelops').empty(); $('#cash_envelops').empty();
...@@ -129,7 +131,21 @@ function set_envelop_dom(envelop, envelop_name, envelop_content_id, envelop_inde ...@@ -129,7 +131,21 @@ function set_envelop_dom(envelop, envelop_name, envelop_content_id, envelop_inde
let envelop_panel = $(`.panel_${envelop_content_id}`); let envelop_panel = $(`.panel_${envelop_content_id}`);
envelop_panel.append(`<button class="btn--danger delete_envelop_button item-fluid" id="update_envelop_${envelop.type}_${envelop_index}">Supprimer l'enveloppe</button>`); envelop_panel.append(`<button class="btn--danger delete_envelop_button item-fluid" id="update_envelop_${envelop.type}_${envelop_index}">Supprimer l'enveloppe</button>`);
envelop_panel.append(`<button class="btn--primary update_envelop_button item-fluid" id="update_envelop_${envelop.type}_${envelop_index}">Modifier</button>`); envelop_panel.append(`
<button
class="btn--primary update_envelop_button item-fluid"
id="update_envelop_${envelop.type}_${envelop_index}"
>
Modifier
</button>`);
envelop_panel.append(`
<button
class="btn--primary add_to_envelop_button item-fluid"
id="add_to_envelop_${envelop.type}_${envelop_index}"
>
Ajouter un paiement ou des parts sociales
</button>`);
$(".update_envelop_button").off("click"); $(".update_envelop_button").off("click");
$(".update_envelop_button").on("click", function() { $(".update_envelop_button").on("click", function() {
let el_id = $(this).attr("id") let el_id = $(this).attr("id")
...@@ -160,6 +176,64 @@ function set_envelop_dom(envelop, envelop_name, envelop_content_id, envelop_inde ...@@ -160,6 +176,64 @@ function set_envelop_dom(envelop, envelop_name, envelop_content_id, envelop_inde
'Supprimer' 'Supprimer'
); );
}); });
$(".add_to_envelop_button").off("click");
$(".add_to_envelop_button").on("click", function() {
let el_id = $(this).attr("id")
.split("_");
envelop_to_update = {
type: el_id[el_id.length-2],
index: el_id[el_id.length-1]
};
let envelop = get_envelop_from_type_index(envelop_to_update.type, envelop_to_update.index);
let envelop_name = get_envelop_name(envelop, 'long');
let modal_add_to_envelop = $('#templates #modal_add_to_envelop');
modal_add_to_envelop.find(".envelop_name").text(envelop_name);
openModal(
modal_add_to_envelop.html(),
() => {},
'',
false,
true,
() => {
envelop_to_update = null;
selected_member = null;
modal.find(".btn-modal-ok").show();
}
);
// No validation button
modal.find(".btn-modal-ok").hide();
modal.find(".add_to_envelop_lines").empty();
// Set action to search for the member
modal.find('.search_member_form').submit(function() {
let search_str = modal.find('.search_member_input').val();
$.ajax({
url: '/members/search/' + search_str + "?search_type=short",
dataType : 'json',
success: function(data) {
members_search_results = data.res;
display_possible_members();
},
error: function() {
err = {
msg: "erreur serveur lors de la recherche de membres",
ctx: 'add_payment_to_envelop.search_members'
};
report_JS_error(err, 'envelops');
alert("Erreur lors de la recherche de membre, il faut ré-essayer plus tard...");
}
});
});
});
} }
} }
...@@ -277,7 +351,7 @@ function set_update_envelop_modal() { ...@@ -277,7 +351,7 @@ function set_update_envelop_modal() {
openModal( openModal(
modal_update_envelop.html(), modal_update_envelop.html(),
() => { () => {
update_envelop(); update_envelop_action();
}, },
'Mettre à jour', 'Mettre à jour',
true, true,
...@@ -314,10 +388,10 @@ function set_update_envelop_modal() { ...@@ -314,10 +388,10 @@ function set_update_envelop_modal() {
} }
/** /**
* Update an envelop in couchdb * Update an envelop data with modal data
*/ */
function update_envelop() { function update_envelop_action() {
if (is_time_to('update_envelop', 1000)) { if (is_time_to('update_envelop_action', 1000)) {
let envelop = get_envelop_from_type_index(envelop_to_update.type, envelop_to_update.index); let envelop = get_envelop_from_type_index(envelop_to_update.type, envelop_to_update.index);
// Update lines amounts // Update lines amounts
...@@ -340,12 +414,22 @@ function update_envelop() { ...@@ -340,12 +414,22 @@ function update_envelop() {
// Envelop comments // Envelop comments
envelop.comments = modal.find('.envelop_comments').val(); envelop.comments = modal.find('.envelop_comments').val();
update_envelop(envelop);
toggle_success_alert("Enveloppe modifiée !");
}
}
/**
* Update an envelop in couchdb
* @param {Object} envelop
*/
function update_envelop(envelop) {
if (is_time_to('update_envelop', 1000)) {
dbc.put(envelop, function callback(err, result) { dbc.put(envelop, function callback(err, result) {
envelop_to_update = null; envelop_to_update = null;
if (!err && result !== undefined) { if (!err && result !== undefined) {
get_envelops(); get_envelops();
toggle_success_alert("Enveloppe modifiée !");
} else { } else {
alert("Erreur lors de la mise à jour de l'enveloppe. Si l'erreur persiste contactez un administrateur svp."); alert("Erreur lors de la mise à jour de l'enveloppe. Si l'erreur persiste contactez un administrateur svp.");
console.log(err); console.log(err);
...@@ -431,7 +515,13 @@ function archive_envelop(type, index) { ...@@ -431,7 +515,13 @@ function archive_envelop(type, index) {
if (error_payments[i].done == false) { if (error_payments[i].done == false) {
error_message += "<p>Erreur lors de l'enregistrement du paiement de <b>" + error_payments[i]['partner_name'] error_message += "<p>Erreur lors de l'enregistrement du paiement de <b>" + error_payments[i]['partner_name']
+ "</b> (id odoo : " + error_payments[i]['partner_id'] + ", valeur à encaisser : " + error_payments[i]['amount'] + "€)."; + "</b> (id odoo : " + error_payments[i]['partner_id'] + ", valeur à encaisser : " + error_payments[i]['amount'] + "€).";
error_message += "<br/><b>L'opération est à reprendre manuellement dans Odoo pour ce paiement.</b></p>"; error_message += "<br/><b>L'opération est à reprendre manuellement dans Odoo pour ce paiement.</b>";
if ('error' in error_payments[i]) {
error_message += `<br/>(error: ${error_payments[i]['error']})`;
}
error_message += "</p>";
} }
} }
...@@ -480,6 +570,194 @@ function get_envelops() { ...@@ -480,6 +570,194 @@ function get_envelops() {
}); });
} }
/**
* Display the members from the search result in the "add payments to envelop" modal
*/
function display_possible_members() {
modal.find('.search_member_results_area').show();
modal.find('.search_member_results').empty();
if (members_search_results.length > 0) {
$(".search_results_text").show();
for (member of members_search_results) {
// Display results (possible members) as buttons
var member_button = '<button class="btn--success btn_possible_member" member_id="'
+ member.id + '">'
+ member.barcode_base + ' - ' + member.name
+ '</button>';
$('.search_member_results').append(member_button);
}
// Set action on member button click
$('.btn_possible_member').on('click', function() {
const mid = $(this).attr('member_id');
selected_member = members_search_results.find(m => m.id == mid);
members_search_results = [];
modal.find('.search_member_input').val('');
modal.find('.search_member_results').empty();
modal.find('.search_member_results_area').hide();
// Adding line for this member in modal...
display_line_add_payment();
});
} else {
$(".search_results_text").hide();
$('.search_member_results').html(`<p>
<i>Aucun résultat ! Vérifiez votre recherche...</i>
</p>`);
}
}
/**
* Display a line for adding a member's payment in the "add payments to envelop" modal
*/
function display_line_add_payment() {
let envelop = get_envelop_from_type_index(envelop_to_update.type, envelop_to_update.index);
// Block adding payment if member is already in the envelop
for (let env_partner_id in envelop.envelop_content) {
if (env_partner_id == selected_member.id) {
alert("Ce membre est déjà dans l'enveloppe, impossible de lui rajouter un paiement.\nVous pouvez modifier le montant de son paiement dans la fenêtre de modification de l'enveloppe.");
return -1;
}
}
modal.find('.search_member_area').hide();
let modal_line = $('#templates #add_to_envelop_line_template');
modal_line.find(".line_partner_name").text(selected_member.name);
modal.find(".add_to_envelop_lines").append(modal_line.html());
modal.find(".add_to_envelop_lines_area").show();
// Add payment button
$('.add_payment_button').off('click');
$('.add_payment_button').on('click', function() {
let amount = parseInt(modal.find(".line_partner_amount").val(), 10);
if (isNaN(amount)) {
modal.find(".line_partner_amount_error").show();
} else {
modal.find(".line_partner_amount_error").hide();
let modal_confirm_add_payment = $('#templates #modal_confirm_add_payment');
modal_confirm_add_payment.find(".amount").text(amount);
modal_confirm_add_payment.find(".member").text(selected_member.name);
modal_confirm_add_payment.find(".envelop").text(get_envelop_name(envelop, 'long'));
openModal(
modal_confirm_add_payment.html(),
() => {
add_payment_to_envelop(amount, envelop);
},
"Confirmer"
);
modal.find(".btn-modal-ok").show();
}
});
// Add shares button
$('.add_shares_button').off('click');
$('.add_shares_button').on('click', function() {
let amount = parseInt(modal.find(".line_partner_amount").val(), 10);
if (isNaN(amount)) {
modal.find(".line_partner_amount_error").show();
} else {
modal.find(".line_partner_amount_error").hide();
let modal_confirm_add_shares = $('#templates #modal_confirm_add_shares');
modal_confirm_add_shares.find(".amount").text(amount);
modal_confirm_add_shares.find(".member").text(selected_member.name);
modal_confirm_add_shares.find(".envelop").text(get_envelop_name(envelop, 'long'));
openModal(
modal_confirm_add_shares.html(),
() => {
add_shares_to_member(amount, envelop);
},
"Confirmer",
false
);
modal.find(".btn-modal-ok").show();
}
});
return null;
}
/**
* Add a payment in an envelop & save in couchdb
* @param {Int} amount
* @param {Object} envelop
* @param {Int} invoice_id
* @param {String} message
*/
function add_payment_to_envelop(amount, envelop, invoice_id=null, message="Paiement ajouté !") {
if (is_time_to('add_payment_to_envelop', 1000)) {
envelop.envelop_content[selected_member.id] = {
partner_name: selected_member.name,
amount: amount
};
if (invoice_id != null) {
envelop.envelop_content[selected_member.id].invoice_id = invoice_id;
}
update_envelop(envelop);
toggle_success_alert(message);
envelop_to_update = null;
selected_member = null;
get_envelops();
}
}
/**
* Send request to add shares & then add payment
* @param {Int} amount
* @param {Object} envelop
*/
function add_shares_to_member(amount, envelop) {
if (is_time_to('add_shares_to_member', 1000)) {
openModal();
data = {
partner_id: selected_member.id,
amount: amount
};
$.ajax({
type: "POST",
url: "/members/add_shares_to_member",
headers: { "X-CSRFToken": getCookie("csrftoken") },
dataType: "json",
traditional: true,
contentType: "application/json; charset=utf-8",
data: JSON.stringify(data),
success: function(response) {
closeModal();
invoice_id = response[0];
add_payment_to_envelop(amount, envelop, invoice_id, "Parts sociales ajoutées !");
},
error: function() {
closeModal();
alert('Un erreur est survenue lors de l\'ajout de parts sociales.');
}
});
}
}
$(document).ready(function() { $(document).ready(function() {
if (typeof must_identify == "undefined" || coop_is_connected()) { if (typeof must_identify == "undefined" || coop_is_connected()) {
get_envelops(); get_envelops();
......
...@@ -40,6 +40,9 @@ def archive_envelop(request): ...@@ -40,6 +40,9 @@ def archive_envelop(request):
'type' : envelop['type'] 'type' : envelop['type']
} }
if 'invoice_id' in envelop['envelop_content'][partner_id]:
data['invoice_id'] = int(envelop['envelop_content'][partner_id]['invoice_id'])
res = m.save_payment(data) res = m.save_payment(data)
except Exception as e: except Exception as e:
res = { res = {
...@@ -75,7 +78,7 @@ def archive_envelop(request): ...@@ -75,7 +78,7 @@ def archive_envelop(request):
coop_logger.error("Cannot attach payment error message to member : %s", str(e)) coop_logger.error("Cannot attach payment error message to member : %s", str(e))
try: try:
# archive envelop from couchdb # archive envelop in couchdb
res_envelop = m.archive_envelop(envelop) res_envelop = m.archive_envelop(envelop)
except Exception as e: except Exception as e:
res_envelop = "error" res_envelop = "error"
......
...@@ -311,6 +311,13 @@ def admin(request): ...@@ -311,6 +311,13 @@ def admin(request):
'module': 'Membres'} 'module': 'Membres'}
return HttpResponse(template.render(context, request)) return HttpResponse(template.render(context, request))
def manage_makeups(request):
""" Administration des membres """
template = loader.get_template('members/admin/manage_makeups.html')
context = {'title': 'BDM - Rattrapages',
'module': 'Membres'}
return HttpResponse(template.render(context, request))
def get_makeups_members(request): def get_makeups_members(request):
""" Récupération des membres qui doivent faire des rattrapages """ """ Récupération des membres qui doivent faire des rattrapages """
res = CagetteMembers.get_makeups_members() res = CagetteMembers.get_makeups_members()
......
...@@ -26,6 +26,9 @@ class CagetteMember(models.Model): ...@@ -26,6 +26,9 @@ class CagetteMember(models.Model):
'display_ftop_points', 'display_std_points', 'display_ftop_points', 'display_std_points',
'is_exempted', 'cooperative_state', 'date_alert_stop'] 'is_exempted', 'cooperative_state', 'date_alert_stop']
m_shoft_default_fields = ['name', 'barcode_base', 'total_partner_owned_share',
'amount_subscription']
def __init__(self, id): def __init__(self, id):
"""Init with odoo id.""" """Init with odoo id."""
self.id = int(id) self.id = int(id)
...@@ -718,7 +721,7 @@ class CagetteMember(models.Model): ...@@ -718,7 +721,7 @@ class CagetteMember(models.Model):
return m_list return m_list
@staticmethod @staticmethod
def search(k_type, key, shift_id=None): def search(k_type, key, shift_id=None, search_type="full"):
"""Search member according 3 types of key.""" """Search member according 3 types of key."""
api = OdooAPI() api = OdooAPI()
if k_type == 'id': if k_type == 'id':
...@@ -733,44 +736,49 @@ class CagetteMember(models.Model): ...@@ -733,44 +736,49 @@ class CagetteMember(models.Model):
cond.append(['is_member', '=', True]) cond.append(['is_member', '=', True])
cond.append(['is_associated_people', '=', True]) cond.append(['is_associated_people', '=', True])
# cond.append(['cooperative_state', '!=', 'unsubscribed']) # cond.append(['cooperative_state', '!=', 'unsubscribed'])
fields = CagetteMember.m_default_fields if search_type == "full":
if not shift_id is None: fields = CagetteMember.m_default_fields
CagetteMember.m_default_fields.append('tmpl_reg_line_ids') if not shift_id is None:
res = api.search_read('res.partner', cond, fields) CagetteMember.m_default_fields.append('tmpl_reg_line_ids')
members = [] res = api.search_read('res.partner', cond, fields)
if len(res) > 0: members = []
for m in res: if len(res) > 0:
keep_it = False for m in res:
if not shift_id is None and len(shift_id) > 0: keep_it = False
# Only member registred to shift_id will be returned if not shift_id is None and len(shift_id) > 0:
if len(m['tmpl_reg_line_ids']) > 0: # Only member registred to shift_id will be returned
cond = [['id', '=', m['tmpl_reg_line_ids'][0]]] if len(m['tmpl_reg_line_ids']) > 0:
fields = ['shift_template_id'] cond = [['id', '=', m['tmpl_reg_line_ids'][0]]]
shift_templ_res = api.search_read('shift.template.registration.line', cond, fields) fields = ['shift_template_id']
if (len(shift_templ_res) > 0 shift_templ_res = api.search_read('shift.template.registration.line', cond, fields)
and if (len(shift_templ_res) > 0
int(shift_templ_res[0]['shift_template_id'][0]) == int(shift_id)): and
keep_it = True int(shift_templ_res[0]['shift_template_id'][0]) == int(shift_id)):
else: keep_it = True
keep_it = True else:
if keep_it is True: keep_it = True
try: if keep_it is True:
img_code = base64.b64decode(m['image_medium']) try:
extension = imghdr.what('', img_code) img_code = base64.b64decode(m['image_medium'])
m['image_extension'] = extension extension = imghdr.what('', img_code)
except Exception as e: m['image_extension'] = extension
coop_logger.info("Img error : %s", e) except Exception as e:
m['state'] = m['cooperative_state'] coop_logger.info("Img error : %s", e)
m['cooperative_state'] = \ m['state'] = m['cooperative_state']
CagetteMember.get_state_fr(m['cooperative_state']) m['cooperative_state'] = \
# member = CagetteMember(m['id'], m['email']) CagetteMember.get_state_fr(m['cooperative_state'])
# m['next_shifts'] = member.get_next_shift() # member = CagetteMember(m['id'], m['email'])
if not m['parent_name'] is False: # m['next_shifts'] = member.get_next_shift()
m['name'] += ' (en binôme avec ' + m['parent_name'] + ')' if not m['parent_name'] is False:
del m['parent_name'] m['name'] += ' (en binôme avec ' + m['parent_name'] + ')'
members.append(m) del m['parent_name']
members.append(m)
return CagetteMember.add_next_shifts_to_members(members)
return CagetteMember.add_next_shifts_to_members(members)
else:
fields = CagetteMember.m_shoft_default_fields
res = api.search_read('res.partner', cond, fields)
return res
@staticmethod @staticmethod
def remove_data_from_CouchDB(request): def remove_data_from_CouchDB(request):
......
.header {
margin-top: 15px;
}
.management_type_buttons {
margin-top: 60px;
}
.management_type_button {
height: 2.2em;
width: 30%;
border-radius: 3px;
margin: 10px;
font-size: 1.3em;
}
.management_type_button_icons {
float: right;
margin: 2px;
}
.login_area {
position: absolute;
top: 5px;
right: 5px;
}
.page_body{
position: relative;
}
.header { .header {
margin: 1.5rem 0; margin: 1rem 0;
} }
.login_area { .login_area {
position: absolute; position: absolute;
top: 0; display: block;
left: 0; top: 5px;
right: 0; right: 5px;
} }
.tabs { #back_to_admin_index {
margin-top: 1em; position: absolute;
margin-bottom: 1em; top: 5px;
overflow: hidden; left: 5px;
}
.tabs .tab {
background-color: #f1f1f1;
border: 1px solid #ccc;
outline: none;
cursor: pointer;
padding: 14px 16px;
transition: 0.3s;
}
.tabs .tab:hover {
background-color: #ccc;
}
.tabs .active {
background-color: transparent;
border: 1px solid #ccc;
border-width: 1px 0 0 0;
}
.tabs .active:hover {
background-color: white;
}
.tab_content {
animation: fadeEffect 1s; /* Fading effect takes 1 second */
}
/* Go from zero to full opacity */
@keyframes fadeEffect {
from {opacity: 0;}
to {opacity: 1;}
}
#tab_makeups_content {
padding: 2rem 0;
} }
#table_top_area { #table_top_area {
......
$(document).ready(function() {
if (coop_is_connected()) {
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
$(".page_content").show();
let location = window.location.href;
$('.management_type_button').on('click', function() {
if (this.id == 'manage_makeups_button') {
window.location.assign(location + "/manage_makeups");
} else if (this.id == 'manage_attached_button') {
console.log('coming soon...');
} else if (this.id == 'manage_shifts_button') {
console.log('coming soon...');
} else if (this.id == 'manage_leaves_button') {
console.log('coming soon...');
}
});
} else {
$(".page_content").hide();
}
});
\ No newline at end of file
...@@ -3,33 +3,6 @@ var makeups_members_table = null, ...@@ -3,33 +3,6 @@ var makeups_members_table = null,
members_search_results = [], members_search_results = [],
selected_rows = []; // Contain members id selected_rows = []; // Contain members id
function switch_active_tab() {
// Set tabs
$('.tab').removeClass('active');
$(this).addClass('active');
// Tabs content
$('.tab_content').hide();
let tab = $(this).attr('id');
if (tab == 'tab_makeups') {
$('#tab_makeups_content').show();
}
load_tab_data();
}
/**
* Load data for the current tab
*/
function load_tab_data() {
let current_tab = $('.tab .active').attr('id');
if (current_tab === 'tab_makeups' && makeups_members === null) {
load_makeups_members();
}
}
/** /**
* Load partners who have makeups to do * Load partners who have makeups to do
...@@ -352,12 +325,16 @@ $(document).ready(function() { ...@@ -352,12 +325,16 @@ $(document).ready(function() {
$(".page_content").show(); $(".page_content").show();
load_makeups_members(); load_makeups_members();
$(".tabs .tab").on('click', switch_active_tab);
} else { } else {
$(".page_content").hide(); $(".page_content").hide();
} }
$('#back_to_admin_index').on('click', function() {
let base_location = window.location.href.split("manage_makeups")[0].slice(0, -1);
window.location.assign(base_location);
});
// Set action to search for the member // Set action to search for the member
$('#search_member_form').submit(function() { $('#search_member_form').submit(function() {
let search_str = $('#search_member_input').val(); let search_str = $('#search_member_input').val();
......
...@@ -35,6 +35,7 @@ urlpatterns = [ ...@@ -35,6 +35,7 @@ urlpatterns = [
url(r'^menu/$', views.menu), url(r'^menu/$', views.menu),
url(r'^verify_final_state$', views.verify_final_state), url(r'^verify_final_state$', views.verify_final_state),
url(r'^update_couchdb_barcodes$', views.update_couchdb_barcodes), url(r'^update_couchdb_barcodes$', views.update_couchdb_barcodes),
url(r'^add_shares_to_member$', views.add_shares_to_member),
# Borne accueil # Borne accueil
url(r'^search/([^\/.]+)/?([0-9]*)', views.search), url(r'^search/([^\/.]+)/?([0-9]*)', views.search),
url(r'^save_photo/([0-9]+)$', views.save_photo, name='save_photo'), url(r'^save_photo/([0-9]+)$', views.save_photo, name='save_photo'),
...@@ -54,6 +55,7 @@ urlpatterns = [ ...@@ -54,6 +55,7 @@ urlpatterns = [
# BDM - members admin # BDM - members admin
url(r'^admin$', admin.admin), url(r'^admin$', admin.admin),
url(r'^admin/manage_makeups$', admin.manage_makeups),
url(r'^get_makeups_members$', admin.get_makeups_members), url(r'^get_makeups_members$', admin.get_makeups_members),
url(r'^update_members_makeups$', admin.update_members_makeups), url(r'^update_members_makeups$', admin.update_members_makeups),
] ]
...@@ -8,7 +8,7 @@ from members.models import CagetteMembers ...@@ -8,7 +8,7 @@ from members.models import CagetteMembers
from members.models import CagetteServices from members.models import CagetteServices
from outils.forms import GenericExportMonthForm from outils.forms import GenericExportMonthForm
import datetime
default_fields = ['name', default_fields = ['name',
'image_medium'] 'image_medium']
...@@ -237,6 +237,8 @@ def update_couchdb_barcodes(request): ...@@ -237,6 +237,8 @@ def update_couchdb_barcodes(request):
def search(request, needle, shift_id): def search(request, needle, shift_id):
"""Search member has been requested.""" """Search member has been requested."""
search_type = request.GET.get('search_type', '')
try: try:
key = int(needle) key = int(needle)
k_type = 'barcode_base' k_type = 'barcode_base'
...@@ -247,7 +249,7 @@ def search(request, needle, shift_id): ...@@ -247,7 +249,7 @@ def search(request, needle, shift_id):
key = needle key = needle
k_type = 'name' k_type = 'name'
res = CagetteMember.search(k_type, key, shift_id) res = CagetteMember.search(k_type, key, shift_id, search_type)
return JsonResponse({'res': res}) return JsonResponse({'res': res})
...@@ -391,6 +393,21 @@ def panel_get_purchases(request): ...@@ -391,6 +393,21 @@ def panel_get_purchases(request):
response = HttpResponse(message) response = HttpResponse(message)
return response return response
def add_shares_to_member(request):
res = {}
try:
data = json.loads(request.body.decode())
partner_id = int(data["partner_id"])
amount = int(data["amount"])
except Exception as e:
res['error'] = "Wrong params"
return JsonResponse(res, safe=False, status=400)
m = CagetteMember(partner_id)
today = datetime.date.today().strftime("%Y-%m-%d")
res = m.create_capital_subscription_invoice(amount, today)
return JsonResponse(res, safe=False)
# # # BDM # # # # # # BDM # # #
def save_partner_info(request): def save_partner_info(request):
......
...@@ -240,7 +240,6 @@ function compute_products_coverage_qties() { ...@@ -240,7 +240,6 @@ function compute_products_coverage_qties() {
order_doc.coeff = coeff; order_doc.coeff = coeff;
if (order_doc.coverage_days != null) { if (order_doc.coverage_days != null) {
compute_and_affect_product_supplier_quantities(coeff, order_doc.coverage_days); compute_and_affect_product_supplier_quantities(coeff, order_doc.coverage_days);
resolve();
} else if (order_doc.targeted_amount != null) { } else if (order_doc.targeted_amount != null) {
const small_step = 0.1, const small_step = 0.1,
max_iter = 182; // Assume that no more than 1/2 year coverage is far enough max_iter = 182; // Assume that no more than 1/2 year coverage is far enough
...@@ -264,11 +263,7 @@ function compute_products_coverage_qties() { ...@@ -264,11 +263,7 @@ function compute_products_coverage_qties() {
if (order_total_value_f != targeted_amount_f && iter < max_iter) { if (order_total_value_f != targeted_amount_f && iter < max_iter) {
step = small_step; // we have gone too far, let's go back, using small step step = small_step; // we have gone too far, let's go back, using small step
days -= step; days -= step;
} }
/* console.log(iter)
console.log(order_total_value_f + '/' + targeted_amount_f)
console.log(days)
console.log(go_on)*/
} else { } else {
if (step == small_step) { if (step == small_step) {
// amount was above the target, let's compute again with the previous value // amount was above the target, let's compute again with the previous value
...@@ -282,13 +277,10 @@ function compute_products_coverage_qties() { ...@@ -282,13 +277,10 @@ function compute_products_coverage_qties() {
iter++; iter++;
} }
}
resolve(); resolve();
}
/* console.log(rder_doc.coverage_days);
console.log(order_doc.targeted_amount)*/
}); });
} }
/** /**
......
"""
Delete makeups_to_do for up_to_date members.
Run this script from the project root with:
$ python -m outils.scripts.delete_makeups_for_uptodate_members
"""
import os
from pathlib import Path
from importlib import import_module
import logging
logging.basicConfig(
level=logging.DEBUG,
format='[%(asctime)s] %(levelname)s - %(message)s',
datefmt='%H:%M:%S'
)
logger = logging.getLogger(__file__)
project_path = Path(__file__).resolve().parents[2]
def get_api():
if not os.environ.get('DJANGO_SETTINGS_MODULE'):
os.environ['DJANGO_SETTINGS_MODULE'] = "outils.settings"
module = import_module('outils.common')
return module.OdooAPI()
def get_concerned_users(api):
cond = [
['cooperative_state', '=', 'up_to_date'],
['makeups_to_do', '>', 0]
]
fields = ['id']
return api.search_read('res.partner', cond, fields)
def main():
api = get_api()
concerned_users = get_concerned_users(api)
logger.info('Number of concerned members %i', len(concerned_users))
for user in concerned_users:
logger.debug("Member: %s is concerned", user.get('name'))
api.update('res.partner', user.get('id'), {'makeups_to_do': 0})
logger.debug("Member: %s has no more make ups to do!",
user.get('name'))
new_concerned_users = get_concerned_users(api)
logger.info('Now the number of concerned members %i',
len(new_concerned_users))
if __name__ == "__main__":
main()
SECRET_KEY = 'Mettre_plein_de_caracteres_aleatoires_iezezezeezezci' SECRET_KEY = 'Mettre_plein_de_caracteres_aleatoires_iezezezeezezci'
ODOO = { ODOO = {
'url': 'http://127.0.0.1:8069' 'url': 'http://127.0.0.1:8069',
'user': 'api', 'user': 'api',
'passwd': 'xxxxxxxxxxxx', 'passwd': 'xxxxxxxxxxxx',
'db': 'bd_test', 'db': 'bd_test',
......
...@@ -175,8 +175,8 @@ String.prototype.pad = function(String, len) { ...@@ -175,8 +175,8 @@ String.prototype.pad = function(String, len) {
var btns = $('<div/>').addClass('btns'); var btns = $('<div/>').addClass('btns');
var btn_ok = $('<button/>').addClass('btn--success'); var btn_ok = $('<button/>').addClass('btn--success btn-modal-ok');
var btn_nok = $('<button/>').addClass('btn--danger') var btn_nok = $('<button/>').addClass('btn--danger btn-modal-nok')
.attr('id', 'modal_closebtn_bottom') .attr('id', 'modal_closebtn_bottom')
.text('Fermer'); .text('Fermer');
......
...@@ -9,9 +9,11 @@ ...@@ -9,9 +9,11 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{%if must_identify %} <div id="admin_connexion_button">
{% include "common/conn_admin.html" %} {%if must_identify %}
{%endif%} {% include "common/conn_admin.html" %}
{%endif%}
</div>
<div id="envelop_cashing_error" class="alert--danger clearfix custom_alert" onClick="toggle_error_alert()"> <div id="envelop_cashing_error" class="alert--danger clearfix custom_alert" onClick="toggle_error_alert()">
<div style="width: 90%" class="fl txtleft" id="error_alert_txt"></div> <div style="width: 90%" class="fl txtleft" id="error_alert_txt"></div>
<div style="width: 10%" class="fr txtright"><i class="fas fa-times"></i></div> <div style="width: 10%" class="fr txtright"><i class="fas fa-times"></i></div>
...@@ -80,13 +82,74 @@ ...@@ -80,13 +82,74 @@
<div class="deleted_line_through"></div> <div class="deleted_line_through"></div>
</div> </div>
</div> </div>
<div id="modal_add_to_envelop">
<div class="modal_add_to_envelop_content">
<h3>Ajouter un paiement ou des parts sociales</h3>
<h4><i class="envelop_name"></i></h4>
<hr>
<div class="search_member_area">
<h4>Rechercher un membre</h4>
<form class="search_member_form" action="javascript:;" method="post">
<input type="text" class="search_member_input" value="" placeholder="Nom ou numéro du coop..." required>
<button type="submit" class="btn--primary" class="search_member_button">Recherche</button>
</form>
</div>
<div class="search_member_results_area" style="display:none;">
<div class="search_results_text">
<p><i>Choisissez parmi les membres trouvés :</i></p>
</div>
<div class="search_member_results"></div>
</div>
<div class="add_to_envelop_lines_area" style="display:none;">
<div class="add_to_envelop_lines">
</div>
<div class="validation_buttons">
<button class="btn--primary add_payment_button">Ajouter le paiement à l'enveloppe</button>
<button class="btn--primary add_shares_button">Ajouter des parts sociales </button>
</div>
</div>
</div>
</div>
<div id="add_to_envelop_line_template">
<div class="add_to_envelop_line">
<div class="partner_name_container">
<span class="line_partner_name"></span>
</div>
<div class="partner_input_container">
<input type="text" class="line_partner_amount" placeholder="Montant">
<div class="line_partner_amount_error">Le montant doit être un nombre entier.</div>
</div>
</div>
</div>
<div id="modal_confirm_add_payment">
<p>
Vous vous apprêtez à ajouter un paiement de <span class="confirm_add_payment_details amount"></span>
du membre <span class="confirm_add_payment_details member"></span>
à l'enveloppe <span class="confirm_add_payment_details envelop"></span>.
</p>
<p><i>
<i class="fas fa-exclamation-triangle"></i> Avertissement si ce.tte membre a plusieurs factures d'ouvertes.<br/>
Au moment de l'encaissement de l'enveloppe, ce paiement sera lié à la plus vieille facture ouverte de ce membre.
</i></p>
</div>
<div id="modal_confirm_add_shares">
<p>
Vous vous apprêtez à ajouter pour <span class="confirm_add_payment_details amount"></span>€ de parts sociales
au membre <span class="confirm_add_payment_details member"></span>.
</p>
<p>
Le paiement sera ajouté à l'enveloppe <span class="confirm_add_payment_details envelop"></span>.
</p>
</div>
</div> </div>
<script src="{% static "js/pouchdb.min.js" %}"></script> <script src="{% static "js/pouchdb.min.js" %}"></script>
<script type="text/javascript"> <script type="text/javascript">
{%if must_identify %} var must_identify = '{{must_identify}}';
var must_identify = true
{%endif%}
var couchdb_dbname = '{{db}}'; var couchdb_dbname = '{{db}}';
var couchdb_server = '{{couchdb_server}}' + couchdb_dbname; var couchdb_server = '{{couchdb_server}}' + couchdb_dbname;
var dbc = new PouchDB(couchdb_dbname); var dbc = new PouchDB(couchdb_dbname);
......
...@@ -2,66 +2,47 @@ ...@@ -2,66 +2,47 @@
{% load static %} {% load static %}
{% block additionnal_css %} {% block additionnal_css %}
<link rel="stylesheet" href="{% static 'css/datatables/datatables.min.css' %}"> <link rel="stylesheet" href="{% static 'css/admin/bdm_index.css' %}">
<link rel="stylesheet" href="{% static 'css/members_admin.css' %}">
<link rel="stylesheet" href="{% static 'jquery-ui-1.12.1/jquery-ui.min.css' %}">
{% endblock %} {% endblock %}
{% block additionnal_scripts %} {% block additionnal_scripts %}
<script type="text/javascript" src="{% static 'jquery-ui-1.12.1/jquery-ui.min.js' %}"></script> <script type="text/javascript" src="{% static 'js/admin/bdm_index.js' %}?v="></script>
<script type="text/javascript" src="{% static 'js/datatables/datatables.min.js' %}"></script> <script type="text/javascript" src="{% static 'js/notify.min.js' %}?v="></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="page_body"> <div class="page_body">
<div class="login_area"> <div class="login_area">
{% include "common/conn_admin.html" %} {% include "common/conn_admin.html" %}
</div>
<div class="header txtcenter">
<h1>Bureau des membres</h1>
</div> </div>
<div class="page_content"> <div class="page_content">
<section class="tabs autogrid"> <div class="header txtcenter">
<div class="button tab active" id="tab_makeups"><h5>Rattrapages</h5></div> <h1>Bienvenue sur l'interface d'administration BDM</h1>
</section>
<div id="tab_makeups_content" class="tab_content">
<div id="table_top_area">
<h3>Liste des membres devant effectuer un rattrapage</h3>
<div class="table_grouped_action">
<button type="button" class="btn--primary" id="decrement_selected_members_makeups">
-1 rattrapage pour les membres sélectionnés
</button>
</div>
</div>
<div class="table_area">
<table id="makeups_members_table" class="display" cellspacing="0" width="100%"></table>
</div>
<div id="add_members_area">
<div id="add_members_form_area">
<h4>Ou, ajouter un rattrapage à un.e membre</h4>
<form id="search_member_form" action="javascript:;" method="post">
<input type="text" id="search_member_input" value="" placeholder="Nom ou numéro du coop..." required>
<button type="submit" class="btn--primary" id="search_member_button">Recherche</button>
</form>
</div>
<div class="search_member_results_area" style="display:none;">
<div class="search_results_text">
<p><i>Choisissez parmi les membres trouvés :</i></p>
</div>
<div class="search_member_results"></div>
</div>
</div>
</div> </div>
</div>
<div id="templates" style="display:none;"></div> <div class="management_type_buttons txtcenter">
<button type="button" class="btn--primary management_type_button" id="manage_makeups_button">
Gestion des rattragapes
<span class="management_type_button_icons"><i class="fas fa-arrow-right"></i></span>
</button><br>
<button type="button" class="btn--primary management_type_button" id="manage_attached_button">
Gestion des binômes
<span class="management_type_button_icons"><i class="fas fa-wrench"></i></span>
{# <span class="management_type_button_icons"><i class="fas fa-arrow-right"></i></span> #}
</button><br>
<button type="button" class="btn--primary management_type_button" id="manage_shifts_button">
Gestion des créneaux
<span class="management_type_button_icons"><i class="fas fa-wrench"></i></span>
{# <span class="management_type_button_icons"><i class="fas fa-arrow-right"></i></span> #}
</button><br>
<button type="button" class="btn--primary management_type_button" id="manage_leaves_button">
Gestion des congés
<span class="management_type_button_icons"><i class="fas fa-wrench"></i></span>
{# <span class="management_type_button_icons"><i class="fas fa-arrow-right"></i></span> #}
</button><br>
</div>
</div>
</div> </div>
<script src='{% static "js/all_common.js" %}?v='></script> <script src="{% static "js/all_common.js" %}?v="></script>
<script src='{% static "js/members_admin.js" %}?v='></script>
{% endblock %} {% endblock %}
{% extends "base.html" %}
{% load static %}
{% block additionnal_css %}
<link rel="stylesheet" href="{% static 'css/datatables/datatables.min.css' %}">
<link rel="stylesheet" href="{% static 'css/admin/manage_makeups.css' %}">
<link rel="stylesheet" href="{% static 'jquery-ui-1.12.1/jquery-ui.min.css' %}">
{% endblock %}
{% block additionnal_scripts %}
<script type="text/javascript" src="{% static 'jquery-ui-1.12.1/jquery-ui.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/datatables.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="page_body">
<div id="back_to_admin_index">
<button type="button" class="btn--danger"><i class="fas fa-arrow-left"></i>&nbsp; Retour</button>
</div>
<div class="login_area">
{% include "common/conn_admin.html" %}
</div>
<div class="header txtcenter">
<h1>Gestion des Rattrapages</h1>
</div>
<div class="page_content">
<div id="table_top_area">
<h3>Liste des membres devant effectuer un rattrapage</h3>
<div class="table_grouped_action">
<button type="button" class="btn--primary" id="decrement_selected_members_makeups">
-1 rattrapage pour les membres sélectionnés
</button>
</div>
</div>
<div class="table_area">
<table id="makeups_members_table" class="display" cellspacing="0" width="100%"></table>
</div>
<div id="add_members_area">
<div id="add_members_form_area">
<h4>Ou, ajouter un rattrapage à un.e membre</h4>
<form id="search_member_form" action="javascript:;" method="post">
<input type="text" id="search_member_input" value="" placeholder="Nom ou numéro du coop..." required>
<button type="submit" class="btn--primary" id="search_member_button">Recherche</button>
</form>
</div>
<div class="search_member_results_area" style="display:none;">
<div class="search_results_text">
<p><i>Choisissez parmi les membres trouvés :</i></p>
</div>
<div class="search_member_results"></div>
</div>
</div>
</div>
<div id="templates" style="display:none;"></div>
</div>
<script src='{% static "js/all_common.js" %}?v='></script>
<script src='{% static "js/admin/manage_makeups.js" %}?v='></script>
{% endblock %}
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