Add validtion to prevent duplicate files for multi-invoice receiving
by comparing sha256 hash values for each file
This commit is contained in:
parent
aaf6f05820
commit
0d30247353
|
@ -24,6 +24,7 @@
|
|||
Forms Core
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import warnings
|
||||
|
@ -659,11 +660,25 @@ class Form(object):
|
|||
'widget': MultiFileUploadWidget(tmpstore)}
|
||||
# if 'required' in kwargs and not kwargs['required']:
|
||||
# kw['missing'] = colander.null
|
||||
if kwargs.get('validate_unique'):
|
||||
kw['validator'] = self.validate_multiple_files_unique
|
||||
files_node = colander.SequenceSchema(file_node, **kw)
|
||||
self.set_node(key, files_node)
|
||||
else:
|
||||
raise ValueError("unknown type for '{}' field: {}".format(key, type_))
|
||||
|
||||
def validate_multiple_files_unique(self, node, value):
|
||||
|
||||
# get SHA256 hash for each file; error if duplicates encountered
|
||||
hashes = {}
|
||||
for fileinfo in value:
|
||||
fp = fileinfo['fp']
|
||||
fp.seek(0)
|
||||
filehash = hashlib.sha256(fp.read()).hexdigest()
|
||||
if filehash in hashes:
|
||||
node.raise_invalid(f"Duplicate file detected: {fileinfo['filename']}")
|
||||
hashes[filehash] = fileinfo
|
||||
|
||||
def set_enum(self, key, enum, empty=None):
|
||||
if enum:
|
||||
self.enums[key] = enum
|
||||
|
@ -906,6 +921,11 @@ class Form(object):
|
|||
return json.dumps({'name': value['filename']})
|
||||
return 'null'
|
||||
|
||||
elif isinstance(value, list) and all([isinstance(f, dfwidget.filedict)
|
||||
for f in value]):
|
||||
return json.dumps([{'name': f['filename']}
|
||||
for f in value])
|
||||
|
||||
app = self.request.rattail_config.get_app()
|
||||
value = app.json_friendly(value)
|
||||
return json.dumps(value)
|
||||
|
|
|
@ -323,6 +323,21 @@ class MultiFileUploadWidget(dfwidget.FileUploadWidget):
|
|||
template = 'multi_file_upload'
|
||||
requirements = ()
|
||||
|
||||
def serialize(self, field, cstruct, **kw):
|
||||
if cstruct in (colander.null, None):
|
||||
cstruct = []
|
||||
|
||||
if cstruct:
|
||||
for fileinfo in cstruct:
|
||||
uid = fileinfo['uid']
|
||||
if uid not in self.tmpstore:
|
||||
self.tmpstore[uid] = fileinfo
|
||||
|
||||
readonly = kw.get("readonly", self.readonly)
|
||||
template = readonly and self.readonly_template or self.template
|
||||
values = self.get_template_values(field, cstruct, kw)
|
||||
return field.renderer(template, **values)
|
||||
|
||||
def deserialize(self, field, pstruct):
|
||||
if pstruct is colander.null:
|
||||
return colander.null
|
||||
|
|
|
@ -570,7 +570,7 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
elif workflow == 'from_multi_invoice':
|
||||
if 'invoice_files' not in f:
|
||||
f.insert_before('invoice_file', 'invoice_files')
|
||||
f.set_type('invoice_files', 'multi_file')
|
||||
f.set_type('invoice_files', 'multi_file', validate_unique=True)
|
||||
f.set_required('invoice_parser_key')
|
||||
f.remove('truck_dump_batch_uuid',
|
||||
'po_number',
|
||||
|
|
Loading…
Reference in a new issue