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
|
Forms Core
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
|
@ -659,11 +660,25 @@ class Form(object):
|
||||||
'widget': MultiFileUploadWidget(tmpstore)}
|
'widget': MultiFileUploadWidget(tmpstore)}
|
||||||
# if 'required' in kwargs and not kwargs['required']:
|
# if 'required' in kwargs and not kwargs['required']:
|
||||||
# kw['missing'] = colander.null
|
# kw['missing'] = colander.null
|
||||||
|
if kwargs.get('validate_unique'):
|
||||||
|
kw['validator'] = self.validate_multiple_files_unique
|
||||||
files_node = colander.SequenceSchema(file_node, **kw)
|
files_node = colander.SequenceSchema(file_node, **kw)
|
||||||
self.set_node(key, files_node)
|
self.set_node(key, files_node)
|
||||||
else:
|
else:
|
||||||
raise ValueError("unknown type for '{}' field: {}".format(key, type_))
|
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):
|
def set_enum(self, key, enum, empty=None):
|
||||||
if enum:
|
if enum:
|
||||||
self.enums[key] = enum
|
self.enums[key] = enum
|
||||||
|
@ -906,6 +921,11 @@ class Form(object):
|
||||||
return json.dumps({'name': value['filename']})
|
return json.dumps({'name': value['filename']})
|
||||||
return 'null'
|
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()
|
app = self.request.rattail_config.get_app()
|
||||||
value = app.json_friendly(value)
|
value = app.json_friendly(value)
|
||||||
return json.dumps(value)
|
return json.dumps(value)
|
||||||
|
|
|
@ -323,6 +323,21 @@ class MultiFileUploadWidget(dfwidget.FileUploadWidget):
|
||||||
template = 'multi_file_upload'
|
template = 'multi_file_upload'
|
||||||
requirements = ()
|
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):
|
def deserialize(self, field, pstruct):
|
||||||
if pstruct is colander.null:
|
if pstruct is colander.null:
|
||||||
return colander.null
|
return colander.null
|
||||||
|
|
|
@ -570,7 +570,7 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
elif workflow == 'from_multi_invoice':
|
elif workflow == 'from_multi_invoice':
|
||||||
if 'invoice_files' not in f:
|
if 'invoice_files' not in f:
|
||||||
f.insert_before('invoice_file', 'invoice_files')
|
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.set_required('invoice_parser_key')
|
||||||
f.remove('truck_dump_batch_uuid',
|
f.remove('truck_dump_batch_uuid',
|
||||||
'po_number',
|
'po_number',
|
||||||
|
|
Loading…
Reference in a new issue