dynamic formsets, sums begin

remotes/origin/yandex
Bachurin Sergey 12 years ago
parent 586bfc9d67
commit bfee03a3be
  1. 6
      project/docs/consts.py
  2. 39
      project/docs/views/base_views.py
  3. 43
      project/static/js/formset-events.js
  4. 212
      project/static/js/lib/jquery.formset.js
  5. 2
      project/templates/base.html
  6. 22
      project/templates/docs/faktura/form.html
  7. 11
      project/templates/docs/faktura/stub_js.html
  8. 15
      project/templates/docs/parts/faktura_form_plat_doc_items.html
  9. 15
      project/templates/docs/parts/faktura_form_tbl_items.html
  10. 10
      project/templates/docs/parts/form_tbl_items.html
  11. 10
      project/templates/docs/stub_js.html

@ -19,9 +19,9 @@ NDS_TYPE_CHOICES = (
)
# ставка НДС
NDS_VALUE_0 = 1
NDS_VALUE_10 = 2
NDS_VALUE_18 = 3
NDS_VALUE_0 = 0
NDS_VALUE_10 = 0.1
NDS_VALUE_18 = 0.18
NDS_VALUE_CHOICES = (
(NDS_VALUE_0, u'Без НДС'),

@ -526,7 +526,8 @@ class BaseItemsViews(BaseViews):
parent_model = self.MODEL,
model = self.ITEM_MODEL,
form = self.ITEM_FORM_CLASS,
extra=2
extra=2,
can_delete=True,
)
def get_list_qs(self):
@ -555,19 +556,15 @@ class BaseItemsViews(BaseViews):
return redirect(self.REDIRECT_AFTER_ADD)
if self.pformset:
pformset = self.pformset(self.request.POST or None)
else:
pformset = None
if self.request.method == 'POST':
form = self.FORM_CLASS(self.request.user, data=self.request.POST)
if '_add_line' in self.request.POST:
post_copy = self.request.POST.copy()
total_forms_key = '%s-TOTAL_FORMS' % self.ITEM_FORM_PREFIX
post_copy[total_forms_key] = int(post_copy[total_forms_key]) + 1
formset = self.ITEM_FORMSET_CLASS(data=post_copy, prefix=self.ITEM_FORM_PREFIX)
else:
formset = self.ITEM_FORMSET_CLASS(data=self.request.POST, prefix=self.ITEM_FORM_PREFIX)
formset = self.ITEM_FORMSET_CLASS(self.request.POST or None, prefix=self.ITEM_FORM_PREFIX)
if '_add_line' not in self.request.POST and form.is_valid() and formset.is_valid():
if form.is_valid() and formset.is_valid() and ((not pformset) or (pformset and pformset.is_valid())):
new_obj = form.save(commit=False)
new_obj.user = self.request.user
new_obj.save()
@ -577,7 +574,7 @@ class BaseItemsViews(BaseViews):
for item in new_items:
item.parent = new_obj
item.save()
if pformset.is_valid():
if pformset and pformset.is_valid():
new_items = pformset.save(commit=False)
for item in new_items:
item.parent = new_obj
@ -645,21 +642,16 @@ class BaseItemsViews(BaseViews):
return redirect(self.REDIRECT_AFTER_EDIT)
obj = self.get_obj(kwargs['id'])
print self.pformset
if self.pformset:
pformset = self.pformset(self.request.POST or None, instance=obj)
if self.request.method == 'POST':
form = self.FORM_CLASS(self.request.user, data=self.request.POST, instance=obj)
if '_add_line' in self.request.POST:
post_copy = self.request.POST.copy()
total_forms_key = '%s-TOTAL_FORMS' % self.ITEM_FORM_PREFIX
post_copy[total_forms_key] = int(post_copy[total_forms_key]) + 1
formset = self.ITEM_FORMSET_CLASS(data=post_copy, prefix=self.ITEM_FORM_PREFIX, instance=obj)
else:
formset = self.ITEM_FORMSET_CLASS(data=self.request.POST, prefix=self.ITEM_FORM_PREFIX, instance=obj)
pformset = None
formset = self.ITEM_FORMSET_CLASS(self.request.POST or None, prefix=self.ITEM_FORM_PREFIX, instance=obj)
form = self.FORM_CLASS(self.request.user, data=self.request.POST or None, instance=obj)
if '_add_line' not in self.request.POST and form.is_valid() and formset.is_valid():
if form.is_valid() and formset.is_valid() and ((not pformset) or (pformset and pformset.is_valid())):
new_obj = form.save()
# сохранить табличную часть
if formset.is_valid():
@ -668,16 +660,13 @@ class BaseItemsViews(BaseViews):
item.parent = new_obj
item.save()
self.update_parent_on_items_save(new_obj, items)
if pformset.is_valid():
if pformset and pformset.is_valid():
items = pformset.save(commit=False)
for item in items:
item.parent = new_obj
item.save()
self.update_parent_on_items_save(new_obj, items)
return redirect(self.REDIRECT_AFTER_EDIT)
else:
form = self.FORM_CLASS(self.request.user, instance=obj)
formset = self.ITEM_FORMSET_CLASS(instance=obj)
dictionary = {
'padeji': self.PADEJI,

@ -0,0 +1,43 @@
$(function($) {
var calc_summa = function (e) {
var $this = $(this);
var qty = parseFloat($this.closest('tr.row').find('td.qty input').val());
var price = parseFloat($this.closest('tr.row').find('td.price input').val());
var total_price = qty * price;
if (total_price) {
$this.closest('tr.row').find('td.total_price input').val(qty * price);
}
}
window.calc_itogo = function (e) {
var itogo_sum = 0;
$('#tbl_items').find('.total_price input:visible').each(function(){
var itogo_sum_row = $(this).val()? parseInt($(this).val()) : 0;
itogo_sum += itogo_sum_row;
})
$('#itogo').html(itogo_sum);
}
var calc_itogo_nds = function() {
$('#itogo_nds_text').html('НДС ' + $('#id_nds_value option:selected').text());
$('#itogo_nds').html($('#id_nds_value').val() * parseInt($('#itogo').html()));
}
window.set_events = function set_events() {
$("#tbl_items tr.row td.qty input").on('keyup', calc_summa);
$("#tbl_items tr.row td.price input").on('keyup', calc_summa);
$("#tbl_items tr.row td.qty input").blur(window.calc_itogo);
$("#tbl_items tr.row td.price input").blur(window.calc_itogo);
$("#tbl_items tr.row td.total_price input").blur(window.calc_itogo);
//$('input[name=nds_type]').change(calc_itogo_nds);
$('#id_nds_value').change(calc_itogo_nds);
//
//$("#tbl_items tr.row td.qty input").blur(calc_itogo_nds);
//$("#tbl_items tr.row td.price input").blur(calc_itogo_nds);
//$("#tbl_items tr.row td.total_price input").blur(calc_itogo_nds);
}
window.set_events();
window.calc_itogo();
calc_itogo_nds();
})

@ -0,0 +1,212 @@
/**
* jQuery Formset 1.2.1
* @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com)
* @requires jQuery 1.2.6 or later
*
* Copyright (c) 2009, Stanislaus Madueke
* All rights reserved.
*
* Licensed under the New BSD License
* See: http://www.opensource.org/licenses/bsd-license.php
*/
;(function($) {
$.fn.formset = function(opts)
{
var options = $.extend({}, $.fn.formset.defaults, opts),
flatExtraClasses = options.extraClasses.join(' '),
$$ = $(this),
$$formsetContainer = $$.parent(),
applyExtraClasses = function(row, ndx) {
if (options.extraClasses) {
row.removeClass(flatExtraClasses);
row.addClass(options.extraClasses[ndx % options.extraClasses.length]);
}
},
updateElementIndex = function(elem, prefix, ndx) {
var idRegex = new RegExp('(' + prefix + '-\\d+-)'),
replacement = prefix + '-' + ndx + '-';
if (elem.attr("for")) elem.attr("for", elem.attr("for").replace(idRegex, replacement));
if (elem.attr('id')) elem.attr('id', elem.attr('id').replace(idRegex, replacement));
if (elem.attr('name')) elem.attr('name', elem.attr('name').replace(idRegex, replacement));
},
hasChildElements = function(row) {
return row.find('input,select,textarea,label').length > 0;
},
insertDeleteLink = function(row) {
if (row.is('TR')) {
// If the forms are laid out in table rows, insert
// the remove button into the last table cell:
row.children(':last').append('<a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + '</a>');
} else if (row.is('UL') || row.is('OL')) {
// If they're laid out as an ordered/unordered list,
// insert an <li> after the last list item:
row.append('<li><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText +'</a></li>');
} else {
// Otherwise, just insert the remove button as the
// last child element of the form's container:
row.append('<a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText +'</a>');
}
row.find('a.' + options.deleteCssClass).click(function() {
var row = $(this).parents('.' + options.formCssClass),
del = row.find('input:hidden[id $= "-DELETE"]');
if (options.beforeremove) options.beforeremove(row)
if (del.length) {
// We're dealing with an inline formset; rather than remove
// this form from the DOM, we'll mark it as deleted and hide
// it, then let Django handle the deleting:
del.val('on');
row.hide();
} else {
row.remove();
// Update the TOTAL_FORMS form count.
// Also update names and IDs for all remaining form controls so they remain in sequence:
var forms = $('.' + options.formCssClass).not('.formset-custom-template');
$('#id_' + options.prefix + '-TOTAL_FORMS').val(forms.length);
for (var i=0, formCount=forms.length; i<formCount; i++) {
applyExtraClasses(forms.eq(i), i);
forms.eq(i).find('input,select,textarea,label').each(function() {
updateElementIndex($(this), options.prefix, i);
});
}
}
checkActionButtons();
// If a post-delete callback was provided, call it with the deleted form:
if (options.removed) options.removed(row);
return false;
});
},
checkActionButtons = function() {
var max_num = parseInt($('#id_' + options.prefix + '-MAX_NUM_FORMS').val()),
formCount = $$formsetContainer.find('.'+options.formCssClass).length
if (options.min_num > 0) {
if (formCount <= options.min_num) {
$$formsetContainer.find('.'+options.deleteCssClass).hide()
} else {
$$formsetContainer.find('.'+options.deleteCssClass+':gt('+(options.min_num-1)+')').show()
}
}
if (max_num > 0) {
if (formCount >= max_num) {
$$formsetContainer.find('.'+options.addCssClass).hide()
} else {
$$formsetContainer.find('.'+options.addCssClass).show()
}
}
};
$$.each(function(i) {
var row = $(this),
del = row.find('input:checkbox[id $= "-DELETE"]');
if (del.length) {
// If you specify "can_delete = True" when creating an inline formset,
// Django adds a checkbox to each form in the formset.
// Replace the default checkbox with a hidden field:
del.before('<input type="hidden" name="' + del.attr('name') +'" id="' + del.attr('id') +'" />');
del.remove();
}
if (hasChildElements(row)) {
insertDeleteLink(row);
row.addClass(options.formCssClass);
applyExtraClasses(row, i);
}
checkActionButtons()
});
if ($$.length) {
var addButton, template;
if (options.formTemplate) {
// If a form template was specified, we'll clone it to generate new form instances:
template = (options.formTemplate instanceof $) ? options.formTemplate : $(options.formTemplate);
template.removeAttr('id').addClass(options.formCssClass).addClass('formset-custom-template');
template.find('input,select,textarea,label').each(function() {
updateElementIndex($(this), options.prefix, 2012);
});
insertDeleteLink(template);
} else {
// Otherwise, use the last form in the formset; this works much better if you've got
// extra (>= 1) forms (thnaks to justhamade for pointing this out):
template = $('.' + options.formCssClass + ':last').clone(true).removeAttr('id');
template.find('input:hidden[id $= "-DELETE"]').remove();
template.find('input,select,textarea,label').each(function() {
var elem = $(this);
// If this is a checkbox or radiobutton, uncheck it.
// This fixes Issue 1, reported by Wilson.Andrew.J:
if (elem.is('input:checkbox') || elem.is('input:radio')) {
elem.attr('checked', false);
} else {
elem.val('');
}
});
}
// FIXME: Perhaps using $.data would be a better idea?
options.formTemplate = template;
if ($$.attr('tagName') == 'TR') {
// If forms are laid out as table rows, insert the
// "add" button in a new table row:
var numCols = $$.eq(0).children().length;
$$.parent().append('<tr><td colspan="' + numCols + '"><a class="' + options.addCssClass + '" href="javascript:void(0)">' + options.addText + '</a></tr>');
addButton = $$.parent().find('tr:last a');
addButton.parents('tr').addClass(options.formCssClass + '-add');
} else {
// Otherwise, insert it immediately after the last form:
$$.filter(':last').after('<a class="' + options.addCssClass + '" href="javascript:void(0)">' + options.addText + '</a>');
addButton = $$.filter(':last').next();
}
addButton.click(function() {
var formCount = parseInt($('#id_' + options.prefix + '-TOTAL_FORMS').val()),
row = options.formTemplate.clone(true).removeClass('formset-custom-template'),
buttonRow = $(this).parents('tr.' + options.formCssClass + '-add').get(0) || this;
applyExtraClasses(row, formCount);
console.log(options);
if (options.beforeadd) options.beforeadd(row);
row.insertBefore($(buttonRow)).show();
row.find('input,select,textarea,label').each(function() {
var elem = $(this);
updateElementIndex(elem, options.prefix, formCount);
if (elem.is('input:checkbox') || elem.is('input:radio')) {
elem.attr('checked', false);
} else {
elem.val('');
}
});
$('#id_' + options.prefix + '-TOTAL_FORMS').val(formCount + 1);
// If a post-add callback was supplied, call it with the added form:
checkActionButtons();
if (options.added) options.added(row);
return false;
});
checkActionButtons();
}
return $$;
}
/* Setup plugin defaults */
var clearErrors = function(row) {
$(row).find('.errorlist').remove();
}
var calc_itogo = function(row) {
window.calc_itogo();
}
$.fn.formset.defaults = {
prefix: 'form', // The form prefix for your django formset
min_num: 0, // Minimum count rows
formTemplate: null, // The jQuery selection cloned to generate new form instances
addText: '+ Добавить строку', // Text for the add link
deleteText: 'Удалить', // Text for the delete link
addCssClass: 'add-row', // CSS class applied to the add link
deleteCssClass: 'delete-row', // CSS class applied to the delete link
formCssClass: 'dynamic-form', // CSS class applied to each form in a formset
extraClasses: [], // Additional CSS classes, which will be applied to each form in turn
beforeadd: clearErrors, // Function called each time before a new form is added
added: window.set_events, // Function called each time a new form is added
beforeremove: null, // Function called each time before a new form is added
removed: calc_itogo // Function called each time a form is deleted
};
})(jQuery)

@ -58,6 +58,8 @@
<script src="{{ STATIC_URL }}js/lib/jquery-ui-1.10.3.custom.min.js"></script>
<script src="{{ STATIC_URL }}js/lib/jquery.ui.datepicker-ru.js"></script>
<script src="{{ STATIC_URL }}js/lib/jquery.form.js"></script>
<script src="{{ STATIC_URL }}js/formset-events.js"></script>
<script src="{{ STATIC_URL }}js/lib/jquery.formset.js"></script>
<script src="{{ STATIC_URL }}js/lib/jquery.blockUI.js"></script>
<script src="{{ STATIC_URL }}js/lib/entertotab.js"></script>

@ -39,26 +39,4 @@
{% endblock %}
{% block js %}
{% comment %}<script type="text/javascript">
{% include 'docs/_common_js/add_line_handler.js' %}
{% include 'docs/_common_js/calc_summa.js' %}
{% include 'docs/_common_js/calc_itogo.js' %}
function set_events() {
$("#tbl_items tr.row td.qty input").blur(calc_summa);
$("#tbl_items tr.row td.price input").blur(calc_summa);
$("#tbl_items tr.row td.qty input").blur(calc_itogo);
$("#tbl_items tr.row td.price input").blur(calc_itogo);
$("#tbl_items tr.row td.total_price input").blur(calc_itogo);
}
$(document).ready(function() {
EnterToTab.init(document.getElementById('aktrabot'), true);
$("#id_doc_date").datepicker({dateFormat:'dd.mm.yy'});
set_events();
calc_itogo();
add_line_handler();
});
</script>{% endcomment %}
{% endblock %}

@ -0,0 +1,11 @@
{# заглушка js #}
<script type="text/javascript">
$(document).ready(function() {
$('tr.plat_form').formset({
prefix: '{{ pformset.prefix }}'
});
$('tr.row_tbl_items').formset({
prefix: '{{ formset.prefix }}'
});
});
</script>

@ -1,17 +1,20 @@
{# табличная часть документа #}
<table id="tbl_items" class="list">
<table id="tbl_plat_items" class="list">
<tbody>
<tr>
<th>К плат. документу</th>
<th style="width: 72px">От</th>
</tr>
{% for pform in pformset %}
<tr class="row {% cycle 'even' 'odd' %}">
<tr class="row {% cycle 'even' 'odd' %} plat_form">
<td class="name">
{{ pform.num.errors }}
{{ pform.num }}
</td>
<td class="doc_date">
{{ pform.doc_date.errors }}
{{ pform.doc_date }}
</td>
{{ pform.id }}
@ -20,13 +23,7 @@
</td>
</tr>
{% endfor %}
<tr>
<td colspan="3">
<input type="submit" name="_add_line" id="id_add_line" class="button-as-link" value="+ Добавить строку" />
</td>
</tr>
</tbody>
</table>
{{ pformset.management_form }}

@ -12,29 +12,37 @@
</tr>
{% for iform in formset %}
<tr class="row {% cycle 'even' 'odd' %}">
<tr class="row_tbl_items row {% cycle 'even' 'odd' %}">
<td class="name">
{{ iform.name.errors }}
{{ iform.name }}
</td>
<td class="units">
{{ iform.units.errors }}
{{ iform.units }}
</td>
<td class="units">
{{ iform.units_kod.errors }}
{{ iform.units_kod }}
</td>
<td class="qty">
{{ iform.qty.errors }}
{{ iform.qty }}
</td>
<td class="price">
{{ iform.price.errors }}
{{ iform.price }}
</td>
<td class="total_price">
{{ iform.total_price.errors }}
{{ iform.total_price }}
</td>
<td class="country_name">
<td class="country_code">
{{ iform.country_code.errors }}
{{ iform.country_code }}
</td>
<td class="country_code">
<td class="country_name">
{{ iform.country_name.errors }}
{{ iform.country_name }}
</td>
{{ iform.id }}
@ -47,7 +55,6 @@
<tr>
<td colspan="3">
<input type="submit" name="_add_line" id="id_add_line" class="button-as-link" value="+ Добавить строку" />
</td>
<td align="right"><b>Итого</b></td>
<td><b id="itogo"></b></td>

@ -11,23 +11,27 @@
</tr>
{% for iform in formset %}
<tr class="row {% cycle 'even' 'odd' %}">
<tr class="row_tbl_items row {% cycle 'even' 'odd' %}">
{% for field in iform %}
{% if field.name == "id" or field.name == "parent" %}
{{ field }}
{% else %}
<td class="{{ field.name }}{% if field.name == "DELETE" %} center{% endif %}">
{% if field.name != "DELETE" %}
<td class="{{ field.name }}">
{{ field.errors }}
{{ field }}
</td>
{% endif %}
{% endif %}
{% endfor %}
<td class="DELETE center">
{{ iform.DELETE }}
</td>
</tr>
{% endfor %}
<tr>
<td colspan="3">
<input type="submit" name="_add_line" id="id_add_line" class="button-as-link" value="+ Добавить строку" />
</td>
<td align="right"><b>Итого</b></td>
<td><b id="itogo"></b></td>

@ -1 +1,11 @@
{# заглушка js #}
<script type="text/javascript">
$(document).ready(function() {
$('tr.plat_form').formset({
prefix: '{{ pformset.prefix }}'
});
$('tr.row_tbl_items').formset({
prefix: '{{ formset.prefix }}'
});
});
</script>
Loading…
Cancel
Save