CRM-15403 fix - Large Batch Data Entry allows multiple submits causing duplicate...
[civicrm-core.git] / templates / CRM / Batch / Form / Entry.js
1 //@todo functions partially moved from tpl but still need an enclosure / cleanup
2 // jslinting etc
3 CRM.$(function($) {
4 $('.selector-rows').change(function () {
5 var options = {
6 'url': CRM.url('civicrm/ajax/batch')
7 };
8
9 $("#Entry").ajaxSubmit(options).on('click', function() {
10 $(this).block();
11 });
12
13 // validate rows
14 checkColumns($(this));
15 });
16 cj('.pledge-adjust-option').click(function(){
17 var blockNo = cj(this).attr('id');
18 cj('select[id="option_type_' + blockNo + '"]').show();
19 cj('select[id="option_type_' + blockNo + '"]').removeAttr('disabled');
20 cj('#field_' + blockNo + '_total_amount').removeAttr('readonly');
21 });
22 $('input[name^="soft_credit_contact_"]').on('change', function(){
23 var rowNum = $(this).attr('id').replace('soft_credit_contact_id_','');
24 var totalAmount = $('#field_'+rowNum+'_total_amount').val();
25 //assign total amount as default soft credit amount
26 $('#soft_credit_amount_'+ rowNum).val(totalAmount);
27 //assign soft credit type default value if any
28 $('#soft_credit_type_'+ rowNum).val($('#sct_default_id').val());
29 });
30
31 // validate rows
32 validateRow();
33
34 //calculate the actual total for the batch
35 calculateActualTotal();
36
37 $('input[id*="_total_amount"]').bind('keyup change', function () {
38 calculateActualTotal();
39 });
40
41 if (CRM.batch.type_id == 1) {
42 // hide all dates if send receipt is checked
43 hideSendReceipt();
44
45 // hide the receipt date if send receipt is checked
46 $('input[id*="][send_receipt]"]').change(function () {
47 showHideReceipt($(this));
48 });
49
50 }
51 else if (CRM.batch.type_id == 2){
52 cj('select[id^="member_option_"]').each(function () {
53 if (cj(this).val() == 1) {
54 cj(this).attr('disabled', true);
55 }
56 });
57
58 // set payment info accord to membership type
59 $('select[id*="_membership_type_0"]').change(function () {
60 setPaymentBlock($(this), null);
61 });
62 $('select[id*="_membership_type_1"]').change(function () {
63 setPaymentBlock($(this), $(this).val());
64 });
65
66 }
67
68 // line breaks between radio buttons and checkboxes
69 $('input.form-radio').next().after('<br />');
70 $('input.form-checkbox').next().after('<br />');
71
72 //set the focus on first element
73 $('#primary_contact_1').focus();
74
75 });
76
77
78
79 function setPaymentBlock(form, memType) {
80 var rowID = form.closest('div.crm-grid-row').attr('entity_id');
81 var dataUrl = CRM.url('civicrm/ajax/memType');
82
83 if (!memType) {
84 memType = cj('select[id="field_' + rowID + '_membership_type_1"]').val();
85 }
86
87 cj.post(dataUrl, {mtype: memType}, function (data) {
88 cj('#field_' + rowID + '_financial_type').val(data.financial_type_id);
89 cj('#field_' + rowID + '_total_amount').val(data.total_amount).change();
90 }, 'json');
91 }
92
93 function hideSendReceipt() {
94 cj('input[id*="][send_receipt]"]').each(function () {
95 showHideReceipt(cj(this));
96 });
97 }
98
99 function showHideReceipt(elem) {
100 var rowID = elem.closest('div.crm-grid-row').attr('entity_id');
101 if (elem.prop('checked')) {
102 cj('.crm-batch-receipt_date-' + rowID).hide();
103 }
104 else {
105 cj('.crm-batch-receipt_date-' + rowID).show();
106 }
107 }
108
109 function validateRow() {
110 cj('.selector-rows').each(function () {
111 checkColumns(cj(this));
112 });
113 }
114
115 function checkColumns(parentRow) {
116 // show valid row icon if all required data is field
117 var validRow = 0;
118 var inValidRow = 0;
119 var errorExists = false;
120 var rowID = parentRow.closest('div.crm-grid-row').attr('entity_id');
121
122 parentRow.find('div .required').each(function () {
123 //special case to handle contact autocomplete select
124 var fieldId = cj(this).attr('id');
125 if (fieldId.substring(0, 16) == 'primary_contact_') {
126 // if display value is set then make sure we also check if contact id is set
127 if (!cj(this).val()) {
128 inValidRow++;
129 }
130 else {
131 if (cj(this).val() && !cj('input[name="primary_contact_select_id[' + rowID + ']"]').val()) {
132 inValidRow++;
133 errorExists = true;
134 }
135 }
136 }
137 else {
138 if (!cj(this).val()) {
139 inValidRow++;
140 }
141 else {
142 if (cj(this).hasClass('error') && !cj(this).hasClass('valid')) {
143 errorExists = true;
144 }
145 else {
146 validRow++;
147 }
148 }
149 }
150 });
151
152 // this means user has entered some data
153 if (errorExists) {
154 parentRow.find("div:first span").prop('class', 'batch-invalid');
155 }
156 else {
157 if (inValidRow == 0 && validRow > 0) {
158 parentRow.find("div:first span").prop('class', 'batch-valid');
159 }
160 else {
161 parentRow.find("div:first span").prop('class', 'batch-edit');
162 }
163 }
164 }
165
166 function calculateActualTotal() {
167 var total = 0;
168 cj('input[id*="_total_amount"]').each(function () {
169 if (cj(this).val()) {
170 total += parseFloat(cj(this).val());
171 }
172 });
173
174 cj('.batch-actual-total').html(formatMoney(total));
175 }
176
177 //money formatting/localization
178 function formatMoney(amount) {
179 var c = 2;
180 var t = CRM.setting.monetaryThousandSeparator;
181 var d = CRM.setting.monetaryDecimalPoint;
182
183 var n = amount,
184 c = isNaN(c = Math.abs(c)) ? 2 : c,
185 d = d == undefined ? "," : d,
186 t = t == undefined ? "." : t, s = n < 0 ? "-" : "",
187 i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "",
188 j = (j = i.length) > 3 ? j % 3 : 0;
189
190 return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
191 }
192
193 /**
194 * This function is use to setdefault elements via ajax
195 *
196 * @param fname string field name
197 * @return void
198 */
199 function setFieldValue(fname, fieldValue, blockNo) {
200 var elementId = cj('[name="field[' + blockNo + '][' + fname + ']"]');
201
202 if (elementId.length == 0) {
203 elementId = cj('input[type=checkbox][name^="field[' + blockNo + '][' + fname + ']"][type!=hidden]');
204 }
205
206 // if element not found than return
207 if (elementId.length == 0) {
208 return;
209 }
210
211 //check if it is date element
212 var isDateElement = elementId.attr('format');
213
214 // check if it is wysiwyg element
215 var editor = elementId.attr('editor');
216
217 //get the element type
218 var elementType = elementId.attr('type');
219
220 // set the value for all the elements, elements needs to be handled are
221 // select, checkbox, radio, date fields, text, textarea, multi-select
222 // wysiwyg editor, advanced multi-select ( to do )
223 if (elementType == 'radio') {
224 if (fieldValue) {
225 elementId.filter("[value=" + fieldValue + "]").prop("checked", true);
226 }
227 else {
228 elementId.removeProp('checked');
229 }
230 }
231 else {
232 if (elementType == 'checkbox') {
233 // handle checkbox
234 elementId.removeProp('checked');
235 if (fieldValue) {
236 cj.each(fieldValue, function (key, value) {
237 cj('input[name="field[' + blockNo + '][' + fname + '][' + value + ']"]').prop('checked', true);
238 });
239 }
240 }
241 else {
242 if (editor) {
243 switch (editor) {
244 case 'ckeditor':
245 var elemtId = elementId.attr('id');
246 oEditor = CKEDITOR.instances[elemtId];
247 oEditor.setData(htmlContent);
248 break;
249 case 'tinymce':
250 var elemtId = element.attr('id');
251 tinyMCE.get(elemtId).setContent(htmlContent);
252 break;
253 case 'joomlaeditor':
254 // TO DO
255 case 'drupalwysiwyg':
256 // TO DO
257 default:
258 elementId.val(fieldValue);
259 }
260 }
261 else {
262 elementId.val(fieldValue);
263 }
264 }
265 }
266
267 // since we use different display field for date we also need to set it.
268 // also check for date time field and set the value correctly
269 if (isDateElement && fieldValue) {
270 setDateFieldValue(fname, fieldValue, blockNo)
271 }
272 }
273
274 function setDateFieldValue(fname, fieldValue, blockNo) {
275 var dateValues = fieldValue.split(' ');
276
277 var actualDateElement = cj('#field_' + blockNo + '_' + fname);
278 var date_format = actualDateElement.attr('format');
279 var altDateFormat = 'yy-mm-dd';
280
281 var actualDateValue = cj.datepicker.parseDate(altDateFormat, dateValues[0]);
282
283 // format date according to display field
284 var hiddenDateValue = cj.datepicker.formatDate('mm/dd/yy', actualDateValue);
285
286 actualDateElement.val(hiddenDateValue);
287
288 var displayDateValue = actualDateElement.val();
289 if (date_format != 'mm/dd/yy') {
290 displayDateValue = cj.datepicker.formatDate(date_format, actualDateValue);
291 }
292
293 cj('#field_' + blockNo + '_' + fname + '_display').val(displayDateValue);
294
295 // need to fix time formatting
296 if (dateValues[1]) {
297 cj('#field_' + blockNo + '_' + fname + '_time').val(dateValues[1].substr(0, 5));
298 }
299 }