django - 发票表格 - 在保存前验证小计和净总额

django - invoice form - validate the sub totals and net total before save

在 inlineformset_factory 的帮助下,我有一个由两个模型组成的发票表格 - Invoice 和 InvoiceItem。

我有以下 jquery 片段,通过读取每件商品的价格、数量、税费来计算个人总计和净总计。

function calculateSubTotal(obj){
    subTotal = 0 

    parentDiv = obj.closest('.formset')

    rate=parseFloat($(parentDiv).find('.rate').val())
    quantity=parseInt($(parentDiv).find('.quantity').val())
    tax=$(parentDiv).find('.tax option:selected').html()//.

    tax=tax.match(/\d+/);
    if(tax)
        tax=parseFloat(tax[0],10)
    else
        return

    if(!isNaN(rate) && !isNaN(quantity) && $.isNumeric(tax)){
        subTotal = rate*quantity*(100+tax)/100
        $(parentDiv).find('.total').val(subTotal)
    }
}

function calculateTotal() {
    subTotal=0
    $('.total').each(function(){
        //console.log($(this).id)
        console.log($(this).val())
        val=parseFloat($(this).val())
        subTotal+=val
    });
    if(!isNaN(subTotal))
        $('#id_total').val(subTotal)
}
$(document).ready(function() {
    $('.formset .form-control').on("blur",function(e){
        calculateSubTotal(this);
        calculateTotal();
    });

});

现在,我"believe",我需要在保存之前在服务器端进行所有这些计算,以防止任何手动correction/error由用户在表单中。 (如果我错了请纠正我)

我该如何继续?

这是我的 form_valid() 的 CreateView。

def form_valid(self, form):
    context = self.get_context_data()
    item_formset = context['item_formset']
    with transaction.atomic():
        form.instance.invoice_type=self.kwargs['invoice_type']
        self.object = form.save(commit=False)
        #self.object.save()
        if item_formset.is_valid():
            forms = item_formset.save(commit=False)

            for form in forms:
                **#calculate sub-total and assign net-total to parentform.instance.total**
            item_formset.instance = self.object
            item_formset.save()

    return super().form_valid(form)

最简洁的方法可能是为 InvoiceInvoiceItem 创建自定义模型方法,然后在您的表单 save() 或视图的 form_vaild().[=18 中调用它们=]

一个例子models.py:

class Invoice(models.Model):
    customer = models.ForeignKey()
    date = models.DateField()
    total = models.DecimalField()

    def process_invoice(self):
        items = InvoiceItem.objects.filter(invoice=self)
        for item in items:
            ... process item ....
            item.save()
        self.total = InvoiceItem.objects.filter(invoice=self).values('line_total').aggregate(tot=Sum('line_total')['tot']
        ... add whatever other logic you need ...
        self.save()

然后在您的 form_valid 中,您将通过 self.object.process_invoice() 调用该方法。

围绕模型的 save()、post-save 信号、表单的 clean() 等进行了大量反复试验,最终得到了一些有用的东西。

这是现在的样子。

  1. 首先使用InvoiceItemFormclean()方法确保每一行的总和是正确的

    def clean(自我): cleaned_data=super(InvoiceItemForm, self).clean()

    total=cleaned_data.get('total')
    if total:
    
        tax=cleaned_data.get('tax')
        calculated_total=price*(100+tax.rate)*quantity/100
        if calculated_total != total:
            raise forms.ValidationError({'total':["Total is incorrect."]})
        return  cleaned_data
    
  2. 接下来,在 CreateViews 的 form_valid() 方法下,遍历表单集中的每个表单并对各个表单求和。将此与主窗体的总值进行比较。

    与 transaction.atomic(): form.instance.invoice_type=self.kwargs['invoice_type'] self.object = form.save(提交=假)

            if item_formset.is_valid() == False:
                return self.render_to_response(self.get_context_data(form=form,item_formset=item_formset ))
    
            subTotal=0
            i=0
            for f in item_formset.forms:
                subTotal += float(f.data['item-'+str(i)+'-total'])
                i=i+1
            if subTotal!= float(form.data['total']):
                form.add_error('total', "Total is incorrect.")
                return self.render_to_response(self.get_context_data(form=form,item_formset=item_formset ))
    

(欢迎任何可能的corrections/simplifications)

谢谢。