Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | {* |
2 | +--------------------------------------------------------------------+ | |
819d0d41 | 3 | | CiviCRM version 4.5 | |
6a488035 | 4 | +--------------------------------------------------------------------+ |
819d0d41 | 5 | | Copyright CiviCRM LLC (c) 2004-2014 | |
6a488035 TO |
6 | +--------------------------------------------------------------------+ |
7 | | This file is a part of CiviCRM. | | |
8 | | | | |
9 | | CiviCRM is free software; you can copy, modify, and distribute it | | |
10 | | under the terms of the GNU Affero General Public License | | |
11 | | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | | |
12 | | | | |
13 | | CiviCRM is distributed in the hope that it will be useful, but | | |
14 | | WITHOUT ANY WARRANTY; without even the implied warranty of | | |
15 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | | |
16 | | See the GNU Affero General Public License for more details. | | |
17 | | | | |
18 | | You should have received a copy of the GNU Affero General Public | | |
19 | | License and the CiviCRM Licensing Exception along | | |
20 | | with this program; if not, contact CiviCRM LLC | | |
21 | | at info[AT]civicrm[DOT]org. If you have questions about the | | |
22 | | GNU Affero General Public License or the licensing of CiviCRM, | | |
23 | | see the CiviCRM license FAQ at http://civicrm.org/licensing | | |
24 | +--------------------------------------------------------------------+ | |
25 | *} | |
26 | <div class="batch-entry form-item"> | |
261371e1 | 27 | <div id="help"> |
b4ae0bee | 28 | {ts}Click Validate & Process below when you've entered all items for the batch. You can also Save & Continue Later at any time. Go to Administer > Customize Data and Screens > Profiles > Reserved Profiles > to add, remove or change the order of columns.{/ts} |
6a488035 | 29 | </div> |
261371e1 | 30 | {if $batchAmountMismatch} |
31 | <div class="status message status-warning"> | |
32 | <div | |
33 | class="icon alert-icon"></div> {ts}Total for amounts entered below does not match the expected batch total.{/ts} | |
34 | </div> | |
35 | <div class="crm-button crm-button_qf_Entry_upload_force-save"> | |
36 | {$form._qf_Entry_upload_force.html} | |
37 | </div> | |
38 | <div class="clear"></div> | |
39 | {/if} | |
40 | <table class="form-layout-compressed batch-totals"> | |
41 | <tr> | |
42 | <td class="label">{ts}Total amount expected{/ts}</td> | |
43 | <td class="right"><span class="batch-expected-total">{$batchTotal|crmMoney}</span></td> | |
44 | </tr> | |
45 | <tr> | |
46 | <td class="label">{ts}Total amount entered{/ts}</td> | |
47 | <td class="right">{$config->defaultCurrencySymbol} <span class="batch-actual-total"></span></td> | |
48 | </tr> | |
49 | </table> | |
50 | ||
51 | <div class="crm-copy-fields crm-grid-table" id="crm-batch-entry-table"> | |
52 | <div class="crm-grid-header"> | |
53 | <div class="crm-grid-cell"> </div> | |
54 | <div class="crm-grid-cell">{ts}Contact{/ts}</div> | |
55 | {if $batchType eq 2 } | |
6a488035 | 56 | <div class="crm-grid-cell"> </div> |
261371e1 | 57 | {/if} |
58 | {foreach from=$fields item=field key=fieldName} | |
59 | <div class="crm-grid-cell"> | |
694ca46e | 60 | {if $field.name|substr:0:11 ne 'soft_credit'} |
261371e1 | 61 | <img src="{$config->resourceBase}i/copy.png" |
62 | alt="{ts 1=$field.title}Click to copy %1 from row one to all rows.{/ts}" | |
63 | fname="{$field.name}" class="action-icon" | |
694ca46e | 64 | title="{ts}Click here to copy the value in row one to ALL rows.{/ts}"/> |
65 | {/if}{$field.title} | |
261371e1 | 66 | </div> |
67 | {/foreach} | |
68 | </div> | |
cdeb4bdf | 69 | |
6a488035 | 70 | {section name='i' start=1 loop=$rowCount} |
261371e1 | 71 | {assign var='rowNumber' value=$smarty.section.i.index} |
72 | <div class="{cycle values="odd-row,even-row"} selector-rows crm-grid-row" entity_id="{$rowNumber}"> | |
6a488035 TO |
73 | <div class="compressed crm-grid-cell"><span class="batch-edit"></span></div> |
74 | {* contact select/create option*} | |
75 | <div class="compressed crm-grid-cell"> | |
ccec9d6b | 76 | {$form.primary_contact_id.$rowNumber.html|crmAddClass:big} |
6a488035 TO |
77 | </div> |
78 | ||
79 | {if $batchType eq 2 } | |
80 | {$form.member_option.$rowNumber.html} | |
81 | {/if} | |
82 | ||
83 | {foreach from=$fields item=field key=fieldName} | |
261371e1 | 84 | {assign var=n value=$field.name} |
85 | {if ( $fields.$n.data_type eq 'Date') or ( in_array( $n, array( 'thankyou_date', 'cancel_date', 'receipt_date', 'receive_date', 'join_date', 'membership_start_date', 'membership_end_date' ) ) ) } | |
86 | <div class="compressed crm-grid-cell"> | |
87 | <span class="crm-batch-{$n}-{$rowNumber}"> | |
88 | {include file="CRM/common/jcalendar.tpl" elementName=$n elementIndex=$rowNumber batchUpdate=1} | |
89 | </span> | |
90 | </div> | |
91 | {elseif $n eq 'soft_credit'} | |
92 | <div class="compressed crm-grid-cell"> | |
ccec9d6b | 93 | {$form.soft_credit_contact_id.$rowNumber.html|crmAddClass:big} |
3e77035d | 94 | {$form.soft_credit_amount.$rowNumber.label} {$form.soft_credit_amount.$rowNumber.html|crmAddClass:eight} |
261371e1 | 95 | </div> |
96 | {elseif in_array( $fields.$n.html_type, array('Radio', 'CheckBox'))} | |
6a488035 | 97 | <div class="compressed crm-grid-cell"> {$form.field.$rowNumber.$n.html}</div> |
261371e1 | 98 | {else} |
6a488035 | 99 | <div class="compressed crm-grid-cell">{$form.field.$rowNumber.$n.html}</div> |
261371e1 | 100 | {/if} |
6a488035 | 101 | {/foreach} |
261371e1 | 102 | </div> |
6a488035 | 103 | {/section} |
261371e1 | 104 | </div> |
105 | <div class="crm-submit-buttons">{if $fields}{$form._qf_Batch_refresh.html}{/if} {$form.buttons.html}</div> | |
6a488035 TO |
106 | </div> |
107 | {literal} | |
108 | <script type="text/javascript"> | |
3cc60a06 | 109 | CRM.$(function($) { |
d5cf4608 | 110 | cj('.selector-rows').change(function () { |
261371e1 | 111 | var options = { |
112 | 'url': {/literal}"{crmURL p='civicrm/ajax/batch' h=0}"{literal} | |
113 | }; | |
6a488035 | 114 | |
261371e1 | 115 | cj("#Entry").ajaxSubmit(options); |
d5cf4608 | 116 | }); |
6a488035 | 117 | |
eac283fc | 118 | cj('#crm-container').on('keyup change', '*.selector-rows', function () { |
261371e1 | 119 | // validate rows |
120 | checkColumns(cj(this)); | |
121 | }); | |
6a488035 | 122 | |
261371e1 | 123 | // validate rows |
124 | validateRow(); | |
6a488035 | 125 | |
261371e1 | 126 | //calculate the actual total for the batch |
127 | calculateActualTotal(); | |
6a488035 | 128 | |
261371e1 | 129 | cj('input[id*="_total_amount"]').bind('keyup change', function () { |
130 | calculateActualTotal(); | |
131 | }); | |
6a488035 | 132 | |
261371e1 | 133 | {/literal}{if $batchType eq 1 }{literal} |
134 | // hide all dates if send receipt is checked | |
135 | hideSendReceipt(); | |
6a488035 | 136 | |
261371e1 | 137 | // hide the receipt date if send receipt is checked |
138 | cj('input[id*="][send_receipt]"]').change(function () { | |
139 | showHideReceipt(cj(this)); | |
140 | }); | |
6a488035 | 141 | |
261371e1 | 142 | {/literal}{else}{literal} |
143 | cj('select[id^="member_option_"]').each(function () { | |
144 | if (cj(this).val() == 1) { | |
6f9cd76f | 145 | cj(this).prop('disabled', true); |
261371e1 | 146 | } |
147 | }); | |
6a488035 | 148 | |
261371e1 | 149 | // set payment info accord to membership type |
150 | cj('select[id*="_membership_type_0"]').change(function () { | |
151 | setPaymentBlock(cj(this), null); | |
152 | }); | |
6a488035 | 153 | |
261371e1 | 154 | cj('select[id*="_membership_type_1"]').change(function () { |
155 | setPaymentBlock(cj(this), cj(this).val()); | |
156 | }); | |
6a488035 | 157 | |
261371e1 | 158 | {/literal}{/if}{literal} |
6a488035 | 159 | |
261371e1 | 160 | // line breaks between radio buttons and checkboxes |
161 | cj('input.form-radio').next().after('<br />'); | |
162 | cj('input.form-checkbox').next().after('<br />'); | |
6a488035 | 163 | |
261371e1 | 164 | //set the focus on first element |
165 | cj('#primary_contact_1').focus(); | |
6a488035 | 166 | |
261371e1 | 167 | }); |
6a488035 | 168 | |
261371e1 | 169 | function setPaymentBlock(form, memType) { |
170 | var rowID = form.closest('div.crm-grid-row').attr('entity_id'); | |
171 | var dataUrl = {/literal}"{crmURL p='civicrm/ajax/memType' h=0}"{literal}; | |
6a488035 | 172 | |
261371e1 | 173 | if (!memType) { |
174 | memType = cj('select[id="field_' + rowID + '_membership_type_1"]').val(); | |
6a488035 TO |
175 | } |
176 | ||
261371e1 | 177 | cj.post(dataUrl, {mtype: memType}, function (data) { |
178 | cj('#field_' + rowID + '_financial_type').val(data.financial_type_id); | |
179 | cj('#field_' + rowID + '_total_amount').val(data.total_amount).change(); | |
180 | }, 'json'); | |
181 | } | |
182 | ||
183 | function hideSendReceipt() { | |
184 | cj('input[id*="][send_receipt]"]').each(function () { | |
185 | showHideReceipt(cj(this)); | |
186 | }); | |
187 | } | |
6a488035 | 188 | |
261371e1 | 189 | function showHideReceipt(elem) { |
190 | var rowID = elem.closest('div.crm-grid-row').attr('entity_id'); | |
191 | if (elem.prop('checked')) { | |
192 | cj('.crm-batch-receipt_date-' + rowID).hide(); | |
193 | } | |
194 | else { | |
195 | cj('.crm-batch-receipt_date-' + rowID).show(); | |
196 | } | |
197 | } | |
198 | ||
199 | function validateRow() { | |
200 | cj('.selector-rows').each(function () { | |
201 | checkColumns(cj(this)); | |
202 | }); | |
203 | } | |
204 | ||
205 | function checkColumns(parentRow) { | |
206 | // show valid row icon if all required data is field | |
207 | var validRow = 0; | |
208 | var inValidRow = 0; | |
209 | var errorExists = false; | |
210 | var rowID = parentRow.closest('div.crm-grid-row').attr('entity_id'); | |
211 | ||
212 | parentRow.find('div .required').each(function () { | |
213 | //special case to handle contact autocomplete select | |
214 | var fieldId = cj(this).attr('id'); | |
215 | if (fieldId.substring(0, 16) == 'primary_contact_') { | |
216 | // if display value is set then make sure we also check if contact id is set | |
217 | if (!cj(this).val()) { | |
218 | inValidRow++; | |
219 | } | |
220 | else { | |
eac283fc | 221 | var contactIdElement = cj('input[name="primary_contact_select_id[' + rowID + ']"]'); |
222 | if (cj(this).val() && !contactIdElement.val()) { | |
261371e1 | 223 | inValidRow++; |
224 | errorExists = true; | |
225 | } | |
eac283fc | 226 | else if (cj(this).val() && contactIdElement.val()) { |
227 | // this is hack to remove error span because we are skipping this for autocomplete fields | |
228 | cj(this).next('span.crm-error').remove(); | |
229 | } | |
261371e1 | 230 | } |
231 | } | |
232 | else { | |
233 | if (!cj(this).val()) { | |
234 | inValidRow++; | |
235 | } | |
236 | else { | |
eac283fc | 237 | if (cj(this).hasClass('error') && (cj(this).hasClass('valid') || cj(this).hasClass('required'))) { |
261371e1 | 238 | errorExists = true; |
239 | } | |
240 | else { | |
241 | validRow++; | |
242 | } | |
243 | } | |
244 | } | |
245 | }); | |
246 | ||
247 | // this means user has entered some data | |
248 | if (errorExists) { | |
249 | parentRow.find("div:first span").prop('class', 'batch-invalid'); | |
250 | } | |
251 | else { | |
252 | if (inValidRow == 0 && validRow > 0) { | |
253 | parentRow.find("div:first span").prop('class', 'batch-valid'); | |
254 | } | |
255 | else { | |
256 | parentRow.find("div:first span").prop('class', 'batch-edit'); | |
257 | } | |
258 | } | |
259 | } | |
260 | ||
261 | function calculateActualTotal() { | |
262 | var total = 0; | |
263 | cj('input[id*="_total_amount"]').each(function () { | |
264 | if (cj(this).val()) { | |
265 | total += parseFloat(cj(this).val()); | |
266 | } | |
267 | }); | |
268 | ||
269 | cj('.batch-actual-total').html(formatMoney(total)); | |
270 | } | |
271 | ||
272 | //money formatting/localization | |
273 | function formatMoney(amount) { | |
274 | var c = 2; | |
275 | var t = '{/literal}{$config->monetaryThousandSeparator}{literal}'; | |
276 | var d = '{/literal}{$config->monetaryDecimalPoint}{literal}'; | |
277 | ||
278 | var n = amount, | |
279 | c = isNaN(c = Math.abs(c)) ? 2 : c, | |
280 | d = d == undefined ? "," : d, | |
281 | t = t == undefined ? "." : t, s = n < 0 ? "-" : "", | |
282 | i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", | |
283 | j = (j = i.length) > 3 ? j % 3 : 0; | |
284 | ||
285 | 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) : ""); | |
286 | } | |
287 | ||
288 | function updateContactInfo(blockNo, prefix) { | |
289 | var contactHiddenElement = 'input[name="' + prefix + 'contact_select_id[' + blockNo + ']"]'; | |
290 | var contactId = cj(contactHiddenElement).val(); | |
291 | ||
292 | var returnProperties = ''; | |
293 | var profileFields = new Array(); | |
294 | {/literal} | |
295 | {if $contactFields} | |
296 | {foreach from=$contactFields item=val key=fldName} | |
297 | var fldName = "{$fldName}"; | |
298 | {literal} | |
299 | if (returnProperties) { | |
300 | returnProperties = returnProperties + ','; | |
301 | } | |
302 | var fld = fldName.split('-'); | |
303 | returnProperties = returnProperties + fld[0]; | |
304 | profileFields[fld[0]] = fldName; | |
305 | {/literal} | |
306 | {/foreach} | |
307 | {/if} | |
308 | {literal} | |
309 | ||
310 | CRM.api('Contact', 'get', { | |
311 | 'sequential': '1', | |
6a488035 TO |
312 | 'contact_id': contactId, |
313 | 'return': returnProperties }, | |
261371e1 | 314 | { success: function (data) { |
315 | cj.each(data.values[0], function (key, value) { | |
316 | // set the values | |
317 | var actualFldName = profileFields[key]; | |
318 | if (key == 'country' || key == 'state_province') { | |
319 | idFldName = key + '_id'; | |
320 | value = data.values[0][idFldName]; | |
321 | } | |
322 | setFieldValue(actualFldName, value, blockNo) | |
323 | }); | |
6a488035 | 324 | |
261371e1 | 325 | // for membership batch entry based on contact we need to enable / disable |
326 | // add membership select | |
327 | {/literal}{if $batchType eq 2}{literal} | |
328 | CRM.api('Membership', 'get', { | |
329 | 'sequential': '1', | |
330 | 'contact_id': contactId, | |
331 | }, | |
332 | { success: function (data) { | |
333 | if (data.count > 0) { | |
334 | //get the information on membership type | |
335 | var membershipTypeId = data.values[0].membership_type_id; | |
336 | var membershipJoinDate = data.values[0].join_date; | |
337 | CRM.api('MembershipType', 'get', { | |
338 | 'sequential': '1', | |
339 | 'id': membershipTypeId | |
340 | }, | |
341 | { success: function (data) { | |
342 | var memTypeContactId = data.values[0].member_of_contact_id; | |
6f9cd76f | 343 | cj('select[id="member_option_' + blockNo + '"]').prop('disabled', false).val(2); |
261371e1 | 344 | cj('select[id="field_' + blockNo + '_membership_type_0"]').val(memTypeContactId).change(); |
345 | cj('select[id="field_' + blockNo + '_membership_type_1"]').val(membershipTypeId).change(); | |
346 | setDateFieldValue('join_date', membershipJoinDate, blockNo) | |
347 | } | |
6a488035 | 348 | }); |
6a488035 | 349 | } |
261371e1 | 350 | } |
6a488035 | 351 | }); |
261371e1 | 352 | {/literal}{/if}{literal} |
353 | } | |
6a488035 | 354 | }); |
261371e1 | 355 | } |
6a488035 TO |
356 | |
357 | /** | |
358 | * This function is use to setdefault elements via ajax | |
359 | * | |
360 | * @param fname string field name | |
361 | * @return void | |
362 | */ | |
261371e1 | 363 | function setFieldValue(fname, fieldValue, blockNo) { |
364 | var elementId = cj('[name="field[' + blockNo + '][' + fname + ']"]'); | |
6a488035 | 365 | |
261371e1 | 366 | if (elementId.length == 0) { |
367 | elementId = cj('input[type=checkbox][name^="field[' + blockNo + '][' + fname + ']"][type!=hidden]'); | |
368 | } | |
6a488035 | 369 | |
261371e1 | 370 | // if element not found than return |
371 | if (elementId.length == 0) { | |
372 | return; | |
373 | } | |
6a488035 | 374 | |
261371e1 | 375 | //check if it is date element |
376 | var isDateElement = elementId.attr('format'); | |
6a488035 | 377 | |
261371e1 | 378 | // check if it is wysiwyg element |
379 | var editor = elementId.attr('editor'); | |
6a488035 | 380 | |
261371e1 | 381 | //get the element type |
382 | var elementType = elementId.attr('type'); | |
6a488035 | 383 | |
261371e1 | 384 | // set the value for all the elements, elements needs to be handled are |
385 | // select, checkbox, radio, date fields, text, textarea, multi-select | |
386 | // wysiwyg editor, advanced multi-select ( to do ) | |
387 | if (elementType == 'radio') { | |
388 | if (fieldValue) { | |
389 | elementId.filter("[value=" + fieldValue + "]").prop("checked", true); | |
390 | } | |
391 | else { | |
392 | elementId.removeProp('checked'); | |
393 | } | |
394 | } | |
395 | else { | |
396 | if (elementType == 'checkbox') { | |
6a488035 TO |
397 | // handle checkbox |
398 | elementId.removeProp('checked'); | |
261371e1 | 399 | if (fieldValue) { |
400 | cj.each(fieldValue, function (key, value) { | |
401 | cj('input[name="field[' + blockNo + '][' + fname + '][' + value + ']"]').prop('checked', true); | |
6a488035 TO |
402 | }); |
403 | } | |
261371e1 | 404 | } |
405 | else { | |
406 | if (editor) { | |
407 | switch (editor) { | |
408 | case 'ckeditor': | |
409 | var elemtId = elementId.attr('id'); | |
410 | oEditor = CKEDITOR.instances[elemtId]; | |
411 | oEditor.setData(htmlContent); | |
412 | break; | |
413 | case 'tinymce': | |
414 | var elemtId = element.attr('id'); | |
415 | tinyMCE.get(elemtId).setContent(htmlContent); | |
416 | break; | |
417 | case 'joomlaeditor': | |
6a488035 | 418 | // TO DO |
261371e1 | 419 | case 'drupalwysiwyg': |
6a488035 | 420 | // TO DO |
261371e1 | 421 | default: |
422 | elementId.val(fieldValue); | |
423 | } | |
424 | } | |
425 | else { | |
426 | elementId.val(fieldValue); | |
6a488035 | 427 | } |
6a488035 | 428 | } |
261371e1 | 429 | } |
6a488035 | 430 | |
261371e1 | 431 | // since we use different display field for date we also need to set it. |
432 | // also check for date time field and set the value correctly | |
433 | if (isDateElement && fieldValue) { | |
434 | setDateFieldValue(fname, fieldValue, blockNo) | |
435 | } | |
6a488035 TO |
436 | } |
437 | ||
261371e1 | 438 | function setDateFieldValue(fname, fieldValue, blockNo) { |
439 | var dateValues = fieldValue.split(' '); | |
6a488035 | 440 | |
261371e1 | 441 | var actualDateElement = cj('#field_' + blockNo + '_' + fname); |
442 | var date_format = actualDateElement.attr('format'); | |
443 | var altDateFormat = 'yy-mm-dd'; | |
6a488035 | 444 | |
261371e1 | 445 | var actualDateValue = cj.datepicker.parseDate(altDateFormat, dateValues[0]); |
6a488035 | 446 | |
261371e1 | 447 | // format date according to display field |
448 | var hiddenDateValue = cj.datepicker.formatDate('mm/dd/yy', actualDateValue); | |
6a488035 | 449 | |
261371e1 | 450 | actualDateElement.val(hiddenDateValue); |
6a488035 | 451 | |
261371e1 | 452 | var displayDateValue = actualDateElement.val(); |
453 | if (date_format != 'mm/dd/yy') { | |
454 | displayDateValue = cj.datepicker.formatDate(date_format, actualDateValue); | |
455 | } | |
6a488035 | 456 | |
261371e1 | 457 | cj('#field_' + blockNo + '_' + fname + '_display').val(displayDateValue); |
6a488035 | 458 | |
261371e1 | 459 | // need to fix time formatting |
460 | if (dateValues[1]) { | |
461 | cj('#field_' + blockNo + '_' + fname + '_time').val(dateValues[1].substr(0, 5)); | |
462 | } | |
6a488035 TO |
463 | } |
464 | ||
465 | </script> | |
466 | {/literal} | |
467 | ||
468 | {*include batch copy js js file*} | |
469 | {include file="CRM/common/batchCopy.tpl"} |