diff --git a/__init__.py b/__init__.py index 9f60d0c..e11b51d 100644 --- a/__init__.py +++ b/__init__.py @@ -18,8 +18,9 @@ def register(): edocument.EdocumentTemplate, edocument.EdocumentExportParams, edocument.EdocumentExportFile, + edocument.EDocumentImportResult, party.Party, - party.PartyIdentifier, + party.PartyConfiguration, stock.Configuration, stock.ConfigurationSequence, product.Template, diff --git a/edocument.py b/edocument.py index b2cafdf..2910c28 100644 --- a/edocument.py +++ b/edocument.py @@ -5,6 +5,7 @@ from trytond.model import ModelSQL, ModelView, fields, Exclude from sql.conditionals import Coalesce from sql.operators import Equal as SqlEqual from trytond.pool import Pool +from trytond.pyson import PYSONEncoder from trytond.transaction import Transaction from trytond.i18n import gettext from trytond.exceptions import UserError @@ -86,11 +87,11 @@ class EdocumentMessage(ModelView, ModelSQL): default_company = Transaction().context.get('company', None) vlist = [x.copy() for x in vlist] for values in vlist: - if config.edocument_sequence: + if not values.get('code') and config.edocument_sequence: values['code'] = config.get_multivalue( 'edocument_sequence', company=values.get('company', default_company)).get() - return super(EdocumentMessage, cls).create(vlist) + return super().create(vlist) class EdocumentMixin(object): @@ -134,25 +135,20 @@ class EdocumentMixin(object): return message.rec_name -class EdocumentImportMixin(EdocumentMixin): +class EdocumentImportMixin(object): + _edi_message_type = '' @classmethod def _get_template_name(cls): pass @classmethod - def create_from_edi(cls, edi_info, template): + def create_object_from_edi(cls, edi_content, template): pass @classmethod - def postprocess(cls, objects): - pass - - @classmethod - def _get_path(cls, message_type): - pool = Pool() - Configuration = pool.get('edocument.configuration.path') - return Configuration._get_path(message_type) + def _postprocess(cls, objects): + return objects @classmethod def _template(cls): @@ -165,56 +161,58 @@ class EdocumentImportMixin(EdocumentMixin): return EdocumentFileManager() @classmethod - def _create_objects_from_edi( - cls, source_uri, error_uri, template=None, target_model=None): + def _create_objects_from_edi(cls, template=None): """ Get objects from edi files """ pool = Pool() - if not target_model: - target_model = cls.__name__ - Object = pool.get(target_model) + Configuration = pool.get('edocument.configuration.path') + ObjectModel = pool.get(cls.__name__) Message = pool.get('edocument.message') + + paths = Configuration._get_path(cls._edi_message_type) + source_uri, error_uri = paths.path, paths.error_path + if not template: template = cls._template() - objects = [] + records = [] + error_files = [] messages = [] - imported = [] + to_delete = [] manager = cls._get_document_manager() for name, info in manager._get_messages(source_uri, error_uri).items(): - created, errors = cls.create_from_edi(info, template) - if created: - message = Message(code=info) - messages.append(message) - imported.append(name) - for value in created: - if 'id' in value: - new_object = Object(value['id']) - else: - new_object = Object() - for k, v in value.items(): - setattr(new_object, k, v) - new_object.message = message - message.origin = new_object - objects.append(new_object) - if imported: - manager._handle_imported(imported) - if errors: - manager._handle_errors(name, errors, error_uri) - Message.save(messages) - Object.save(objects) - return objects + try: + record, errors = cls.create_object_from_edi(info, template) + except RuntimeError: + continue + else: + if errors: + error_files.append(manager._handle_errors( + name, errors, error_uri)) + else: + if record: + message = Message( + code=os.path.basename(os.path.splitext(name)[0]), + message=info, + origin=record) + messages.append(message) + records.append(record) + to_delete.append(name) + + ObjectModel.save(records) + if messages: + Message.save(messages) + + records = cls._postprocess(records) + + if to_delete: + manager._handle_imported(to_delete) + + return records, error_files @classmethod - def get_objects_from_edi(cls, message_type, target_model=None): - '''Get objects from edi''' - conf = cls._get_path(message_type) - results = cls._create_objects_from_edi( - conf.path, - conf.error_path, - target_model=target_model) - cls.postprocess(results) - return results + def get_objects_from_edi(cls): + return cls._create_objects_from_edi() class EdocumentFileManager(object): @@ -222,7 +220,7 @@ class EdocumentFileManager(object): @classmethod def _read_file(cls, filename): - with open(filename, encoding='utf-8') as file: + with open(filename, 'r', encoding='cp1252') as file: return file.read() @classmethod @@ -240,6 +238,7 @@ class EdocumentFileManager(object): os.path.splitext(os.path.basename(fname))[0])) with open(error_fname, 'w') as fp: fp.write('\n'.join(errors)) + return error_fname @classmethod def _handle_imported(cls, imported): @@ -247,6 +246,80 @@ class EdocumentFileManager(object): os.remove(file) +class EDocumentImportResult(ModelView): + """Import EDIFACT Result""" + __name__ = 'edifact_import.result' + + error_file = fields.Binary('Errors', filename='error_filename', + readonly=True) + error_filename = fields.Char('Filename') + message = fields.Text('Message', readonly=True) + + +def import_edocument_mixin(model_name, result_field): + + class ImportEDocumentMixin(object): + + start = StateTransition() + errors = StateView('edifact_import.result', + 'edocument_edifact.import_result_view_form', [ + Button('OK', 'pre_open', 'tryton-ok', default=True), + ]) + pre_open = StateTransition() + + @property + def result_records(self): + return getattr(self.errors, result_field, []) + + @result_records.setter + def result_records(self, value): + setattr(self.errors, result_field, [s.id for s in (value or [])]) + + def transition_start(self): + pool = Pool() + ActiveModel = pool.get(model_name) + Configuration = pool.get('edocument.configuration.path') + + paths = Configuration._get_path(ActiveModel._edi_message_type) + + results, error_files = ActiveModel.get_objects_from_edi() + self.result_records = results + if error_files: + error_file = os.path.join(paths.error_path, 'error_file') + with open(error_file, 'w') as ef: + for file in error_files: + ef.write('%s:\n' % file.split('/')[-1].split('error_')[-1]) + with open(file, 'r') as f: + for line in f: + ef.write('\t- %s' % line) + ef.write('\n') + with open(error_file, mode='r') as f: + self.errors.error_file = f.read() + return 'errors' + return 'pre_open' + + def default_errors(self, fields): + return { + 'error_file': self.errors.error_file, + 'error_filename': 'EDI_error.txt', + 'message': self.errors.error_file, + result_field: [r.id for r in self.result_records] + } + + def transition_pre_open(self): + return 'open_' + + def do_open_(self, action): + if not self.result_records: + return + action['pyson_domain'] = PYSONEncoder().encode([ + ('id', 'in', [s.id for s in self.result_records]) + ]) + return action, {} + + return ImportEDocumentMixin + + class EdocumentExportMixin(object): _message_type = '' diff --git a/edocument.xml b/edocument.xml index 68e7da1..5a310ba 100644 --- a/edocument.xml +++ b/edocument.xml @@ -192,5 +192,12 @@ form export_params_form + + + + edifact_import.result + form + import_result_form + \ No newline at end of file diff --git a/locale/es.po b/locale/es.po index bd898a4..f29208d 100644 --- a/locale/es.po +++ b/locale/es.po @@ -400,4 +400,16 @@ msgstr "Mensajes de Documentación Electrónica" msgctxt "model:ir.ui.menu,name:menu_edocument_message" msgid "Messages" -msgstr "Mensajes" \ No newline at end of file +msgstr "Mensajes" + +msgctxt "model:edifact_import.result,name:" +msgid "Import EDIFACT Result" +msgstr "Resultado importar EDIFACT" + +msgctxt "field:edifact_import.result,error_file:" +msgid "Errors" +msgstr "Errores" + +msgctxt "view:edifact_import.result:" +msgid "The import of EDI files has encountered the following errors:" +msgstr "La importación de ficheros EDI ha encontrado los siguientes errores:" \ No newline at end of file diff --git a/party.py b/party.py index dabdf3f..a3ba0c6 100644 --- a/party.py +++ b/party.py @@ -29,15 +29,16 @@ class Party(metaclass=PoolMeta): ], 'Assigned Code') -class PartyIdentifier(metaclass=PoolMeta): - __name__ = 'party.identifier' +class PartyConfiguration(metaclass=PoolMeta): + __name__ = 'party.configuration' @classmethod - def get_types(cls): - return super().get_types() + [ + def __setup__(cls): + super().__setup__() + cls.identifier_types.selection.extend([ ('EDI_sender', 'EDI Sender'), ('EDI_receiver', 'EDI Receiver'), ('EDI_supplier', 'EDI Supplier'), ('EDI_payer', 'EDI Payer'), ('EDI_buyer', 'EDI Buyer'), - ('EDI_invoice', 'EDI Invoice')] + ('EDI_invoice', 'EDI Invoice')]) diff --git a/view/import_result_form.xml b/view/import_result_form.xml new file mode 100644 index 0000000..1391c68 --- /dev/null +++ b/view/import_result_form.xml @@ -0,0 +1,13 @@ + + +
+ +