Commit a7f142e4 by Etienne Freiss

develop

parent 5cfdcae6
Pipeline #1897 passed with stage
in 1 minute 28 seconds
...@@ -1198,7 +1198,7 @@ class CagetteServices(models.Model): ...@@ -1198,7 +1198,7 @@ class CagetteServices(models.Model):
).total_seconds() / 60 > default_acceptable_minutes_after_shift_begins ).total_seconds() / 60 > default_acceptable_minutes_after_shift_begins
if with_members is True: if with_members is True:
cond = [['id', 'in', s['registration_ids']], ['state', 'not in', ['cancel', 'waiting', 'draft']]] cond = [['id', 'in', s['registration_ids']], ['state', 'not in', ['cancel', 'waiting', 'draft']]]
fields = ['partner_id', 'shift_type', 'state', 'is_late'] fields = ['partner_id', 'shift_type', 'state', 'is_late', 'associate_registered']
members = api.search_read('shift.registration', cond, fields) members = api.search_read('shift.registration', cond, fields)
s['members'] = sorted(members, key=lambda x: x['partner_id'][0]) s['members'] = sorted(members, key=lambda x: x['partner_id'][0])
if len(s['members']) > 0: if len(s['members']) > 0:
...@@ -1207,22 +1207,27 @@ class CagetteServices(models.Model): ...@@ -1207,22 +1207,27 @@ class CagetteServices(models.Model):
for m in s['members']: for m in s['members']:
mids.append(m['partner_id'][0]) mids.append(m['partner_id'][0])
cond = [['parent_id', 'in', mids]] cond = [['parent_id', 'in', mids]]
fields = ['parent_id', 'name'] fields = ['id', 'parent_id', 'name']
associated = api.search_read('res.partner', cond, fields) associated = api.search_read('res.partner', cond, fields)
if len(associated) > 0: if len(associated) > 0:
for m in s['members']: for m in s['members']:
for a in associated: for a in associated:
if int(a['parent_id'][0]) == int(m['partner_id'][0]): if int(a['parent_id'][0]) == int(m['partner_id'][0]):
m['partner_name'] = m['partner_id'][1]
m['partner_id'][1] += ' en binôme avec ' + a['name'] m['partner_id'][1] += ' en binôme avec ' + a['name']
m['associate_name'] = str(a['id']) + ' - ' + a['name']
return services return services
@staticmethod @staticmethod
def registration_done(registration_id, overrided_date=""): def registration_done(registration_id, overrided_date="", typeAction=""):
"""Equivalent to click present in presence form.""" """Equivalent to click present in presence form."""
api = OdooAPI() api = OdooAPI()
f = {'state': 'done'} f = {'state': 'done'}
if(typeAction != "normal" and typeAction != ""):
f['associate_registered'] = typeAction
late_mode = getattr(settings, 'ENTRANCE_WITH_LATE_MODE', False) late_mode = getattr(settings, 'ENTRANCE_WITH_LATE_MODE', False)
if late_mode is True: if late_mode is True:
# services = CagetteServices.get_services_at_time('14:28',0, with_members=False) # services = CagetteServices.get_services_at_time('14:28',0, with_members=False)
...@@ -1244,7 +1249,14 @@ class CagetteServices(models.Model): ...@@ -1244,7 +1249,14 @@ class CagetteServices(models.Model):
return api.update('shift.registration', [int(registration_id)], f) return api.update('shift.registration', [int(registration_id)], f)
@staticmethod @staticmethod
def record_rattrapage(mid, sid, stid): def registration_cancel(registration_id, overrided_date=""):
api = OdooAPI()
f = {'state': 'open'}
return api.update('shift.registration', [int(registration_id)], f)
@staticmethod
def record_rattrapage(mid, sid, stid, typeAction):
"""Add a shift registration for member mid. """Add a shift registration for member mid.
(shift sid, shift ticket stid) (shift sid, shift ticket stid)
...@@ -1260,6 +1272,8 @@ class CagetteServices(models.Model): ...@@ -1260,6 +1272,8 @@ class CagetteServices(models.Model):
"state": 'open'} "state": 'open'}
reg_id = api.create('shift.registration', fields) reg_id = api.create('shift.registration', fields)
f = {'state': 'done'} f = {'state': 'done'}
if(typeAction != "normal" and typeAction != ""):
f['associate_registered'] = typeAction
return api.update('shift.registration', [int(reg_id)], f) return api.update('shift.registration', [int(reg_id)], f)
@staticmethod @staticmethod
...@@ -1275,6 +1289,20 @@ class CagetteServices(models.Model): ...@@ -1275,6 +1289,20 @@ class CagetteServices(models.Model):
# let authorized people time to set presence for those who came in late # let authorized people time to set presence for those who came in late
end_date = now - datetime.timedelta(hours=2) end_date = now - datetime.timedelta(hours=2)
api = OdooAPI() api = OdooAPI()
# Let's start by adding an extra shift to associated member who came together
cond = [['date_begin', '>=', date_24h_before.isoformat()],
['date_begin', '<=', end_date.isoformat()],
['state', '=', 'done'], ['associate_registered', '=', 'both']]
fields = ['state', 'partner_id', 'date_begin']
res = api.search_read('shift.registration', cond, fields)
for r in res:
cond = [['id', '=', r['partner_id'][0]]]
fields = ['id','extra_shift_done']
res = api.search_read('res.partner', cond, fields)
f = {'extra_shift_done': res[0]['extra_shift_done'] + 1 }
api.update('res.partner', [r['partner_id'][0]], f)
absence_status = 'excused' absence_status = 'excused'
res_c = api.search_read('ir.config_parameter', res_c = api.search_read('ir.config_parameter',
[['key', '=', 'lacagette_membership.absence_status']], [['key', '=', 'lacagette_membership.absence_status']],
......
...@@ -44,8 +44,9 @@ h1 .member_name {font-weight: bold;} ...@@ -44,8 +44,9 @@ h1 .member_name {font-weight: bold;}
.members_list {list-style: none;} .members_list {list-style: none;}
.members_list li {display:block;margin-bottom:5px;} .members_list li {display:block;margin-bottom:5px;}
.members_list li.btn--inverse {background: #449d44; cursor:not-allowed; color: #FFF; } .members_list li.btn--inverse {background: #449d44; color: #FFF; }
.members_list li.btn--inverse.late {background-color: #de9b00; color: white} .members_list li.btn--inverse.late {background-color: #de9b00; color: white}
.members_list li.btn--inverse.both {background-color: #0275d8 ; color: white}
#service_entry_success {font-size: x-large;} #service_entry_success {font-size: x-large;}
#service_entry_success .explanations {margin: 25px 0; font-size: 18px;} #service_entry_success .explanations {margin: 25px 0; font-size: 18px;}
...@@ -64,7 +65,7 @@ h1 .member_name {font-weight: bold;} ...@@ -64,7 +65,7 @@ h1 .member_name {font-weight: bold;}
#service_en_cours .info a {line-height: 24px; font-size:14px; margin-top:15px;} #service_en_cours .info a {line-height: 24px; font-size:14px; margin-top:15px;}
.outside_list a {margin-left:15px;} .outside_list a {margin-left:15px;}
#rattrapage_1 .advice {margin-top:15px;} #rattrapage_1 .advice {margin-top:15px;}
.btn.present {background:#50C878;} .btn.present {background:#50C878; color: white !important; font-weight: bold;}
.btn.return {background: #ff3333; color:#FFF !important; margin-top:25px;} .btn.return {background: #ff3333; color:#FFF !important; margin-top:25px;}
.msg-big {font-size: xx-large; background: #fff; padding:25px; text-align: center;} .msg-big {font-size: xx-large; background: #fff; padding:25px; text-align: center;}
......
...@@ -31,12 +31,14 @@ var search_field = $('input[name="search_string"]'); ...@@ -31,12 +31,14 @@ var search_field = $('input[name="search_string"]');
var shift_title = $('#current_shift_title'); var shift_title = $('#current_shift_title');
var shift_members = $('#current_shift_members'); var shift_members = $('#current_shift_members');
var service_validation = $('#service_validation'); var service_validation = $('#service_validation');
var associated_service_validation = $('#associated_service_validation');
var validation_last_call = 0; var validation_last_call = 0;
var rattrapage_wanted = $('[data-next="rattrapage_1"]'); var rattrapage_wanted = $('[data-next="rattrapage_1"]');
var webcam_is_attached = false; var webcam_is_attached = false;
var photo_advice = $('#photo_advice'); var photo_advice = $('#photo_advice');
var photo_studio = $('#photo_studio'); var photo_studio = $('#photo_studio');
var coop_info = $('.coop-info'); var coop_info = $('.coop-info');
var service_data = null;
const missed_begin_msg = $('#missed_begin_msg').html(); const missed_begin_msg = $('#missed_begin_msg').html();
...@@ -264,7 +266,7 @@ function get_simple_service_name(s) { ...@@ -264,7 +266,7 @@ function get_simple_service_name(s) {
} }
function move_service_validation_to(page) { function move_service_validation_to(page) {
service_validation.find('.btn').data('stid', '0'); service_data.stid=0;
page.find('.validation_wrapper') page.find('.validation_wrapper')
.append(service_validation.detach()); .append(service_validation.detach());
} }
...@@ -285,10 +287,14 @@ function fill_service_entry(s) { ...@@ -285,10 +287,14 @@ function fill_service_entry(s) {
var li_data = ""; var li_data = "";
if (e.state == "done") { if (e.state == "done") {
li_data = ' data-rid="'+e.id+'" data-mid="'+e.partner_id[0]+'"';
li_class += "--inverse"; li_class += "--inverse";
if (e.is_late == true) { if (e.is_late == true) {
li_class += " late"; li_class += " late";
} }
if (e.associate_registered=='both') {
li_class += " both";
}
} else { } else {
li_data = ' data-rid="'+e.id+'" data-mid="'+e.partner_id[0]+'"'; li_data = ' data-rid="'+e.id+'" data-mid="'+e.partner_id[0]+'"';
} }
...@@ -321,13 +327,29 @@ function clean_service_entry() { ...@@ -321,13 +327,29 @@ function clean_service_entry() {
function fill_service_validation(rid, coop_num_name, coop_id) { function fill_service_validation(rid, coop_num_name, coop_id) {
var coop_name_elts = coop_num_name.split(' - '); var coop_name_elts = coop_num_name.split(' - ');
for (member of loaded_services[0].members) {
if (member.id ==rid) {
if (member.associate_name) {
pages.service_entry_validation.find('#service_validation').hide();
pages.service_entry_validation.find('#associated_service_validation').show();
pages.service_entry_validation.find('#associated_btn').text(member.associate_name);
pages.service_entry_validation.find('#partner_btn').text(member.partner_name);
} else {
pages.service_entry_validation.find('#associated_service_validation').hide();
pages.service_entry_validation.find('#service_validation').show();
}
}
}
service_data={
rid: rid,
sid: selected_service.id,
mid: coop_id};
pages.service_entry_validation.find('span.member_name').text(coop_name_elts[1]); pages.service_entry_validation.find('span.member_name').text(coop_name_elts[1]);
move_service_validation_to(pages.service_entry_validation); move_service_validation_to(pages.service_entry_validation);
service_validation.find('.btn')
.data('rid', rid)
.data('sid', selected_service.id)
.data('mid', coop_id);
} }
function select_possible_service() { function select_possible_service() {
...@@ -372,7 +394,6 @@ function get_service_entry_data() { ...@@ -372,7 +394,6 @@ function get_service_entry_data() {
dataType : 'json' dataType : 'json'
}) })
.done(function(rData) { .done(function(rData) {
//console.log(rData);
info_place.text(''); info_place.text('');
var page_title = pages.service_entry.find('h1'); var page_title = pages.service_entry.find('h1');
...@@ -397,6 +418,7 @@ function get_service_entry_data() { ...@@ -397,6 +418,7 @@ function get_service_entry_data() {
page_title.text('Quel est ton service ?'); page_title.text('Quel est ton service ?');
} else { } else {
loaded_services = rData.res;
fill_service_entry(rData.res[0]); fill_service_entry(rData.res[0]);
} }
} }
...@@ -443,22 +465,21 @@ function fill_service_entry_sucess(member) { ...@@ -443,22 +465,21 @@ function fill_service_entry_sucess(member) {
} }
function record_service_presence() { function record_service_presence(e) {
var d = new Date(); var d = new Date();
var elapsed_since_last_call = d.getTime() - validation_last_call; var elapsed_since_last_call = d.getTime() - validation_last_call;
if (elapsed_since_last_call > 10000) { if (elapsed_since_last_call > 1000) {
loading2.show(); loading2.show();
validation_last_call = d.getTime(); validation_last_call = d.getTime();
var clicked = service_validation.find('.btn'); var rid = service_data.rid;
var rid = clicked.data('rid'); var mid = service_data.mid;
var mid = clicked.data('mid'); var sid = service_data.sid;
var sid = clicked.data('sid'); var stid = service_data.stid;
var stid = clicked.data('stid');
post_form( post_form(
'/members/service_presence/', '/members/service_presence/',
{'mid': mid, 'rid': rid, 'sid': sid, 'stid' : stid}, {'mid': mid, 'rid': rid, 'sid': sid, 'stid' : stid, 'cancel': false, 'type': e.data.type},
function(err, rData) { function(err, rData) {
if (!err) { if (!err) {
var res = rData.res; var res = rData.res;
...@@ -480,6 +501,28 @@ function record_service_presence() { ...@@ -480,6 +501,28 @@ function record_service_presence() {
} }
} }
function cancel_service_presence(mid, rid) {
var d = new Date();
var elapsed_since_last_call = d.getTime() - validation_last_call;
if (elapsed_since_last_call > 1000) {
loading2.show();
validation_last_call = d.getTime();
var sid = selected_service.id;
post_form(
'/members/service_presence/',
{'mid': mid, 'rid': rid, 'sid': sid, 'stid' : 0, 'cancel': true},
function(err) {
if (!err) {
get_service_entry_data();
}
loading2.hide();
}
);
}
}
function fill_rattrapage_2() { function fill_rattrapage_2() {
pages.rattrapage_2.find('span.member_name').text(current_displayed_member.name); pages.rattrapage_2.find('span.member_name').text(current_displayed_member.name);
var msg = "Bienvenue pour ton rattrapage !"; var msg = "Bienvenue pour ton rattrapage !";
...@@ -494,13 +537,11 @@ function fill_rattrapage_2() { ...@@ -494,13 +537,11 @@ function fill_rattrapage_2() {
msg = "Tu es en désincrit.e ... La situation doit être réglée avez le Bureau des Membres"; msg = "Tu es en désincrit.e ... La situation doit être réglée avez le Bureau des Membres";
} else { } else {
move_service_validation_to(pages.rattrapage_2); move_service_validation_to(pages.rattrapage_2);
service_data = {
rid : 0,
service_validation.find('.btn') sid : selected_service.id,
.data('rid', 0) stid : shift_ticket_id,
.data('sid', selected_service.id) mid : current_displayed_member.id};
.data('stid', shift_ticket_id)
.data('mid', current_displayed_member.id);
} }
pages.rattrapage_2.find('h2').text(msg); pages.rattrapage_2.find('h2').text(msg);
...@@ -641,7 +682,10 @@ $('.btn[data-next]').click(function() { ...@@ -641,7 +682,10 @@ $('.btn[data-next]').click(function() {
}); });
service_validation.on("click", ".btn", record_service_presence); service_validation.on("click", ".btn", {type:'normal'}, record_service_presence);
associated_service_validation.on("click", "#associated_btn", {type:'associate'}, record_service_presence);
associated_service_validation.on("click", "#partner_btn", {type:'partner'}, record_service_presence);
associated_service_validation.on("click", "#both_btn", {type:'both'}, record_service_presence);
shift_members.on("click", '.btn[data-rid]', function() { shift_members.on("click", '.btn[data-rid]', function() {
var clicked = $(this); var clicked = $(this);
...@@ -653,6 +697,14 @@ shift_members.on("click", '.btn[data-rid]', function() { ...@@ -653,6 +697,14 @@ shift_members.on("click", '.btn[data-rid]', function() {
}); });
shift_members.on("click", '.btn--inverse', function() {
var clicked = $(this);
var rid = clicked.data('rid');
var mid = clicked.data('mid');
cancel_service_presence(mid, rid);
});
pages.shopping_entry.on('css', function() { pages.shopping_entry.on('css', function() {
photo_advice.hide(); photo_advice.hide();
photo_studio.hide(); photo_studio.hide();
......
...@@ -277,6 +277,9 @@ def record_service_presence(request): ...@@ -277,6 +277,9 @@ def record_service_presence(request):
mid = int(request.POST.get("mid", 0)) # member id mid = int(request.POST.get("mid", 0)) # member id
sid = int(request.POST.get("sid", 0)) # shift id sid = int(request.POST.get("sid", 0)) # shift id
stid = int(request.POST.get("stid", 0)) # shift_ticket_id stid = int(request.POST.get("stid", 0)) # shift_ticket_id
cancel = request.POST.get("cancel") == 'true'
typeAction = str(request.POST.get("type"))
app_env = getattr(settings, 'APP_ENV', "prod") app_env = getattr(settings, 'APP_ENV', "prod")
if (rid > -1 and mid > 0): if (rid > -1 and mid > 0):
overrided_date = "" overrided_date = ""
...@@ -286,14 +289,15 @@ def record_service_presence(request): ...@@ -286,14 +289,15 @@ def record_service_presence(request):
if o_date: if o_date:
overrided_date = re.sub(r'(%20)',' ', o_date.group(1)) overrided_date = re.sub(r'(%20)',' ', o_date.group(1))
if(not cancel):
# rid = 0 => C'est un rattrapage, sur le service # rid = 0 => C'est un rattrapage, sur le service
if sid > 0 and stid > 0: if sid > 0 and stid > 0:
# Add member to service and take presence into account # Add member to service and take presence into account
res['rattrapage'] = CagetteServices.record_rattrapage(mid, sid, stid) res['rattrapage'] = CagetteServices.record_rattrapage(mid, sid, stid, typeAction)
if res['rattrapage'] is True: if res['rattrapage'] is True:
res['update'] = 'ok' res['update'] = 'ok'
else: else:
if (CagetteServices.registration_done(rid, overrided_date) is True): if (CagetteServices.registration_done(rid, overrided_date, typeAction) is True):
res['update'] = 'ok' res['update'] = 'ok'
else: else:
res['update'] = 'ko' res['update'] = 'ko'
...@@ -308,6 +312,8 @@ def record_service_presence(request): ...@@ -308,6 +312,8 @@ def record_service_presence(request):
del m['shifts'] del m['shifts']
m['next_shift'] = next_shift m['next_shift'] = next_shift
res['member'] = m res['member'] = m
else: CagetteServices.registration_cancel(rid, overrided_date)
except Exception as e: except Exception as e:
res['error'] = str(e) res['error'] = str(e)
return JsonResponse({'res': res}) return JsonResponse({'res': res})
......
...@@ -69,7 +69,6 @@ ...@@ -69,7 +69,6 @@
display: flex; display: flex;
align-items: center; align-items: center;
margin-left: 15px; margin-left: 15px;
margin: 0.75rem 0;
border-radius: 5px; border-radius: 5px;
margin-right: 15px; margin-right: 15px;
} }
...@@ -86,10 +85,13 @@ ...@@ -86,10 +85,13 @@
display: flex; display: flex;
align-items: center; align-items: center;
margin-left: 15px; margin-left: 15px;
margin: 0.75rem 0;
border-radius: 5px; border-radius: 5px;
} }
.selectable_shift{
margin: 1rem 0;
}
/* -- Calendar screen, makeups message */ /* -- Calendar screen, makeups message */
......
...@@ -36,7 +36,7 @@ function add_or_change_shift(new_shift_id) { ...@@ -36,7 +36,7 @@ function add_or_change_shift(new_shift_id) {
+'&idPartner=' + partner_data.partner_id +'&idPartner=' + partner_data.partner_id
+ '&shift_type=' + partner_data.shift_type + '&shift_type=' + partner_data.shift_type
+ '&verif_token=' + partner_data.verif_token; + '&verif_token=' + partner_data.verif_token;
} else if(partner_data.is_associated_people === "True" && block_actions_for_attached_people === "False") { } else if (partner_data.is_associated_people === "True" && block_actions_for_attached_people === "False") {
tData = 'idNewShift=' + new_shift_id tData = 'idNewShift=' + new_shift_id
+'&idPartner=' + partner_data.parent_id +'&idPartner=' + partner_data.parent_id
+ '&shift_type=' + partner_data.shift_type + '&shift_type=' + partner_data.shift_type
...@@ -178,7 +178,9 @@ function init_shifts_list() { ...@@ -178,7 +178,9 @@ function init_shifts_list() {
shift_line_template.find('.affect_associate_registered').attr('id', 'shift_id_'+shift.id); shift_line_template.find('.affect_associate_registered').attr('id', 'shift_id_'+shift.id);
if (shift.associate_registered==="both") { if (shift.associate_registered==="both") {
shift_line_template.find('.affect_associate_registered').text("Les deux"); shift_line_template.find('.affect_associate_registered').text("Les deux");
shift_line_template.find('.affect_associate_registered').addClass('btn--success');
} else if (shift.associate_registered==="partner") { } else if (shift.associate_registered==="partner") {
shift_line_template.find('.affect_associate_registered').addClass('btn--success');
if (partner_data.associated_partner_id !== "False") { if (partner_data.associated_partner_id !== "False") {
shift_line_template.find('.affect_associate_registered').text(partner_data.name); shift_line_template.find('.affect_associate_registered').text(partner_data.name);
} else { } else {
...@@ -186,6 +188,7 @@ function init_shifts_list() { ...@@ -186,6 +188,7 @@ function init_shifts_list() {
} }
} else if (shift.associate_registered==="associate") { } else if (shift.associate_registered==="associate") {
shift_line_template.find('.affect_associate_registered').addClass('btn--success');
if (partner_data.associated_partner_id !== "False") { if (partner_data.associated_partner_id !== "False") {
shift_line_template.find('.affect_associate_registered').text(partner_data.associated_partner_name); shift_line_template.find('.affect_associate_registered').text(partner_data.associated_partner_name);
} else { } else {
...@@ -193,10 +196,13 @@ function init_shifts_list() { ...@@ -193,10 +196,13 @@ function init_shifts_list() {
} }
} else { } else {
shift_line_template.find('.affect_associate_registered').text("A déterminer"); shift_line_template.find('.affect_associate_registered').text("A déterminer");
shift_line_template.find('.affect_associate_registered').addClass('btn--danger');
} }
} }
$("#shifts_list").append(shift_line_template.html()); $("#shifts_list").append(shift_line_template.html());
shift_line_template.find('.affect_associate_registered').removeClass('btn--danger');
shift_line_template.find('.affect_associate_registered').removeClass('btn--success');
} }
$(".selectable_shift_line").on("click", function(e) { $(".selectable_shift_line").on("click", function(e) {
......
...@@ -181,6 +181,18 @@ ...@@ -181,6 +181,18 @@
<span class="loading2"><img width="75" src="/static/img/Pedro_luis_romani_ruiz.gif" alt="Chargement en cours...." /></span> <span class="loading2"><img width="75" src="/static/img/Pedro_luis_romani_ruiz.gif" alt="Chargement en cours...." /></span>
<div class="col-2"></div> <div class="col-2"></div>
</section> </section>
<div id="associated_service_validation">
<p class="col-6 txtcenter">Qui est présent à ce service ?</p>
<section class="col-6 grid-6 has-gutter">
<div class="col-2"></div>
<a id="associated_btn" class=" btn present">Membre</a>
<a id="partner_btn" class=" btn present">Associé</a>
<a id="both_btn" class=" btn present">Les deux</a>
<span class="loading2"><img width="75" src="/static/img/Pedro_luis_romani_ruiz.gif" alt="Chargement en cours...." /></span>
<div class="col-2"></div>
</section>
</div>
</div> </div>
<div class="col-2"></div> <div class="col-2"></div>
<a class="btn col-2 return" data-next="service_entry">Retour</a> <a class="btn col-2 return" data-next="service_entry">Retour</a>
......
...@@ -36,14 +36,14 @@ ...@@ -36,14 +36,14 @@
</div> </div>
<div id="selectable_shift_line_template"> <div id="selectable_shift_line_template">
<div class="d-flex"> <div class="d-flex selectable_shift">
<div class="selectable_shift_line btn--primary"> <div class="selectable_shift_line btn--primary">
<input type="checkbox" class="checkbox"> <input type="checkbox" class="checkbox">
<div class="selectable_shift_line_text"> <div class="selectable_shift_line_text">
<span class="shift_line_date"></span> - <span class="shift_line_time"></span> <span class="shift_line_date"></span> - <span class="shift_line_time"></span>
</div> </div>
</div> </div>
<div class="affect_associate_registered button--warning"> <div class="affect_associate_registered">
</div> </div>
</div> </div>
......
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