diff --git a/addons/account/edi/invoice.py b/addons/account/edi/invoice.py
index 751eb8656f7..e84bec6614d 100644
--- a/addons/account/edi/invoice.py
+++ b/addons/account/edi/invoice.py
@@ -71,16 +71,6 @@ INVOICE_EDI_STRUCT = {
class account_invoice(osv.osv, EDIMixin):
_inherit = 'account.invoice'
- def action_invoice_sent(self, cr, uid, ids, context=None):
- """"Override this method to add a link to mail"""
- if context is None:
- context = {}
- invoice_objs = self.browse(cr, uid, ids, context=context)
- edi_token = self.pool.get('edi.document').export_edi(cr, uid, invoice_objs, context = context)[0]
- web_root_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
- ctx = dict(context, edi_web_url_view=edi.EDI_VIEW_WEB_URL % (web_root_url, cr.dbname, edi_token))
- return super(account_invoice, self).action_invoice_sent(cr, uid, ids, context=ctx)
-
def edi_export(self, cr, uid, records, edi_struct=None, context=None):
"""Exports a supplier or customer invoice"""
edi_struct = dict(edi_struct or INVOICE_EDI_STRUCT)
@@ -136,7 +126,7 @@ class account_invoice(osv.osv, EDIMixin):
self._edi_requires_attributes(('company_id','company_address','type'), edi_document)
res_partner = self.pool.get('res.partner')
- src_company_id, src_company_name = edi_document.pop('company_id')
+ _, src_company_name = edi_document.pop('company_id')
invoice_type = edi_document['type']
partner_value = {}
diff --git a/addons/account/edi/invoice_action_data.xml b/addons/account/edi/invoice_action_data.xml
index 53fa03a6902..1ad023c3a60 100644
--- a/addons/account/edi/invoice_action_data.xml
+++ b/addons/account/edi/invoice_action_data.xml
@@ -1,17 +1,6 @@
- You can view the invoice document, download it and pay online using the following link: -
- View Invoice - % if object.company_id.paypal_account and object.type in ('out_invoice', 'in_refund'): <% comp_name = quote(object.company_id.name) diff --git a/addons/account/test/test_edi_invoice.yml b/addons/account/test/test_edi_invoice.yml index 80dc098e770..3d57f8dc608 100644 --- a/addons/account/test/test_edi_invoice.yml +++ b/addons/account/test/test_edi_invoice.yml @@ -38,11 +38,12 @@ - Then I export the customer invoice - - !python {model: edi.document}: | + !python {model: edi.edi}: | + import json invoice_pool = self.pool.get('account.invoice') invoice = invoice_pool.browse(cr, uid, ref("invoice_edi_1")) - token = self.export_edi(cr, uid, [invoice]) - assert token, 'Invalid EDI Token' + edi_doc = self.generate_edi(cr, uid, [invoice]) + assert isinstance(json.loads(edi_doc)[0], dict), 'EDI doc should be a JSON dict' - Then I import a sample EDI document of another customer invoice - diff --git a/addons/edi/__init__.py b/addons/edi/__init__.py index 46fabbc2fd0..71ee8aaa53d 100644 --- a/addons/edi/__init__.py +++ b/addons/edi/__init__.py @@ -20,14 +20,14 @@ ############################################################################## import logging -import models -import edi_service -from models.edi import EDIMixin, edi_document +from . import models +from . import edi_service +from models.edi import EDIMixin, edi _logger = logging.getLogger(__name__) # web try: - import controllers + import openerp.addons.web.controllers except ImportError: _logger.warn( """Could not load openerp-web section of EDI, EDI will not behave correctly diff --git a/addons/edi/__openerp__.py b/addons/edi/__openerp__.py index b06c01eb03b..72ccb32d701 100644 --- a/addons/edi/__openerp__.py +++ b/addons/edi/__openerp__.py @@ -36,12 +36,10 @@ documentation at http://doc.openerp.com. 'website': 'http://www.openerp.com', 'depends': ['base', 'email_template'], 'icon': '/edi/static/src/img/knowledge.png', - 'data': ['security/ir.model.access.csv'], 'test': ['test/edi_partner_test.yml'], 'js': ['static/src/js/edi.js'], 'css': ['static/src/css/edi.css'], 'qweb': ['static/src/xml/*.xml'], - 'installable': True, 'auto_install': False, } diff --git a/addons/edi/controllers/main.py b/addons/edi/controllers/main.py index c62fef19cba..0322099b6f3 100644 --- a/addons/edi/controllers/main.py +++ b/addons/edi/controllers/main.py @@ -1,31 +1,10 @@ -import json -import textwrap - -import simplejson -import werkzeug.wrappers - -import openerp.addons.web.http as openerpweb +import openerp.addons.web.common.http as openerpweb import openerp.addons.web.controllers.main as webmain class EDI(openerpweb.Controller): - # http://hostname:8069/edi/view?db=XXXX&token=XXXXXXXXXXX # http://hostname:8069/edi/import_url?url=URIEncodedURL _cp_path = "/edi" - def template(self, req, mods='web,edi'): - d = {} - d["js"] = "\n".join(''%i for i in webmain.manifest_list(req, mods, 'js')) - d["css"] = "\n".join(''%i for i in webmain.manifest_list(req, mods, 'css')) - d["modules"] = simplejson.dumps(mods.split(',')) - return d - - @openerpweb.httprequest - def view(self, req, db, token): - d = self.template(req) - d["init"] = 's.edi.edi_view("%s","%s");'%(db,token) - r = webmain.html_template % d - return r - @openerpweb.httprequest def import_url(self, req, url): d = self.template(req) @@ -33,46 +12,6 @@ class EDI(openerpweb.Controller): r = webmain.html_template % d return r - @openerpweb.httprequest - def download(self, req, db, token): - result = req.session.proxy('edi').get_edi_document(db, token) - response = werkzeug.wrappers.Response( result, headers=[('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', len(result))]) - return response - - @openerpweb.httprequest - def download_attachment(self, req, db, token): - result = req.session.proxy('edi').get_edi_document(db, token) - doc = json.loads(result)[0] - attachment = doc['__attachments'] and doc['__attachments'][0] - if attachment: - result = attachment["content"].decode('base64') - import email.Utils as utils - - # Encode as per RFC 2231 - filename_utf8 = attachment['file_name'] - filename_encoded = "%s=%s" % ('filename*', - utils.encode_rfc2231(filename_utf8, 'utf-8')) - response = werkzeug.wrappers.Response(result, headers=[('Content-Type', 'application/pdf'), - ('Content-Disposition', 'inline; ' + filename_encoded), - ('Content-Length', len(result))]) - return response - - @openerpweb.httprequest - def binary(self, req, db, token, field_path="company_address.logo", content_type='image/png'): - result = req.session.proxy('edi').get_edi_document(db, token) - doc = json.loads(result)[0] - for name in field_path.split("."): - doc = doc[name] - result = doc.decode('base64') - response = werkzeug.wrappers.Response(result, headers=[('Content-Type', content_type), - ('Content-Length', len(result))]) - return response - - @openerpweb.jsonrequest - def get_edi_document(self, req, db, token): - result = req.session.proxy('edi').get_edi_document(db, token) - return json.loads(result) - @openerpweb.jsonrequest def import_edi_url(self, req, url): result = req.session.proxy('edi').import_edi_url(req.session._db, req.session._uid, req.session._password, url) @@ -80,6 +19,4 @@ class EDI(openerpweb.Controller): return {"action": webmain.clean_action(req, result[0][2])} return True -# - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/edi/edi_service.py b/addons/edi/edi_service.py index 63fe38786e0..fd911139170 100644 --- a/addons/edi/edi_service.py +++ b/addons/edi/edi_service.py @@ -20,8 +20,8 @@ ############################################################################## import logging -import netsvc import openerp +import openerp.netsvc as netsvc _logger = logging.getLogger(__name__) @@ -34,10 +34,10 @@ class edi(netsvc.ExportService): try: registry = openerp.modules.registry.RegistryManager.get(db_name) assert registry, 'Unknown database %s' % db_name - edi_document = registry['edi.document'] + edi = registry['edi.edi'] cr = registry.db.cursor() res = None - res = getattr(edi_document, method_name)(cr, *method_args) + res = getattr(edi, method_name)(cr, *method_args) cr.commit() except Exception: _logger.exception('Failed to execute EDI method %s with args %r.', method_name, method_args) @@ -46,9 +46,6 @@ class edi(netsvc.ExportService): cr.close() return res - def exp_get_edi_document(self, db_name, edi_token): - return self._edi_dispatch(db_name, 'get_document', 1, edi_token) - def exp_import_edi_document(self, db_name, uid, passwd, edi_document, context=None): return self._edi_dispatch(db_name, 'import_edi', uid, edi_document, None) @@ -59,9 +56,6 @@ class edi(netsvc.ExportService): if method in ['import_edi_document', 'import_edi_url']: (db, uid, passwd ) = params[0:3] openerp.service.security.check(db, uid, passwd) - elif method in ['get_edi_document']: - # No security check for these methods - pass else: raise KeyError("Method not found: %s." % method) fn = getattr(self, 'exp_'+method) diff --git a/addons/edi/models/edi.py b/addons/edi/models/edi.py index 6d11db1c52c..29e19f2da3b 100644 --- a/addons/edi/models/edi.py +++ b/addons/edi/models/edi.py @@ -30,9 +30,9 @@ import urllib2 import openerp import openerp.release as release -import netsvc -import pooler -from osv import osv,fields,orm +import openerp.netsvc as netsvc +from openerp.modules.registry import RegistryManager +from openerp.osv import osv, fields from tools.translate import _ from tools.safe_eval import safe_eval as eval _logger = logging.getLogger(__name__) @@ -74,16 +74,9 @@ def last_update_for(record): return False -class edi_document(osv.osv): - _name = 'edi.document' - _description = 'EDI Document' - _columns = { - 'name': fields.char("EDI token", size = 128, help="Unique identifier for retrieving an EDI document."), - 'document': fields.text("Document", help="EDI document content") - } - _sql_constraints = [ - ('name_uniq', 'unique (name)', 'EDI Tokens must be unique!') - ] +class edi(osv.AbstractModel): + _name = 'edi.edi' + _description = 'EDI Subsystem' def new_edi_token(self, cr, uid, record): """Return a new, random unique token to identify this model record, @@ -109,7 +102,7 @@ class edi_document(osv.osv): """Generates a final EDI document containing the EDI serialization of the given records, which should all be instances of a Model that has the :meth:`~.edi` mixin. The document is not saved in the - database, this is done by :meth:`~.export_edi`. + database. :param list(browse_record) records: records to export as EDI :return: UTF-8 encoded string containing the serialized records @@ -120,19 +113,6 @@ class edi_document(osv.osv): edi_list += record_model_obj.edi_export(cr, uid, [record], context=context) return self.serialize(edi_list) - def get_document(self, cr, uid, edi_token, context=None): - """Retrieve the EDI document corresponding to the given edi_token. - - :return: EDI document string - :raise: ValueError if requested EDI token does not match any know document - """ - _logger.debug("get_document(%s)", edi_token) - edi_ids = self.search(cr, uid, [('name','=', edi_token)], context=context) - if not edi_ids: - raise ValueError('Invalid EDI token: %s.' % edi_token) - edi = self.browse(cr, uid, edi_ids[0], context=context) - return edi.document - def load_edi(self, cr, uid, edi_documents, context=None): """Import the given EDI document structures into the system, using :meth:`~.import_edi`. @@ -171,38 +151,18 @@ class edi_document(osv.osv): """ return json.loads(edi_documents_string) - def export_edi(self, cr, uid, records, context=None): - """Export the given database records as EDI documents, stores them - permanently with a new unique EDI token, for later retrieval via :meth:`~.get_document`, - and returns the list of the new corresponding ``ir.edi.document`` records. - - :param records: list of browse_record of any model - :return: list of IDs of the new ``ir.edi.document`` entries, in the same - order as the provided ``records``. - """ - exported_ids = [] - for record in records: - document = self.generate_edi(cr, uid, [record], context) - token = self.new_edi_token(cr, uid, record) - self.create(cr, uid, { - 'name': token, - 'document': document - }, context=context) - exported_ids.append(token) - return exported_ids - def import_edi(self, cr, uid, edi_document=None, edi_url=None, context=None): """Import a JSON serialized EDI Document string into the system, first retrieving it from the given ``edi_url`` if provided. - :param str|unicode edi_document: UTF-8 string or unicode containing JSON-serialized + :param str|unicode edi: UTF-8 string or unicode containing JSON-serialized EDI Document to import. Must not be provided if ``edi_url`` is given. - :param str|unicode edi_url: URL where the EDI document (same format as ``edi_document``) + :param str|unicode edi_url: URL where the EDI document (same format as ``edi``) may be retrieved, without authentication. """ if edi_url: - assert not edi_document, 'edi_document must not be provided if edi_url is given.' + assert not edi_document, 'edi must not be provided if edi_url is given.' edi_document = urllib2.urlopen(edi_url).read() assert edi_document, 'EDI Document is empty!' edi_documents = self.deserialize(edi_document) @@ -215,10 +175,10 @@ class EDIMixin(object): ``edi_import()`` and ``edi_export()`` methods to implement their specific behavior, based on the primitives provided by this mixin.""" - def _edi_requires_attributes(self, attributes, edi_document): - model_name = edi_document.get('__imported_model') or edi_document.get('__model') or self._name + def _edi_requires_attributes(self, attributes, edi): + model_name = edi.get('__imported_model') or edi.get('__model') or self._name for attribute in attributes: - assert edi_document.get(attribute),\ + assert edi.get(attribute),\ 'Attribute `%s` is required in %s EDI documents.' % (attribute, model_name) # private method, not RPC-exposed as it creates ir.model.data entries as @@ -318,7 +278,6 @@ class EDIMixin(object): :return: list of dicts containing boilerplate EDI metadata for each record, at the corresponding index from ``records``. """ - data_ids = [] ir_attachment = self.pool.get('ir.attachment') results = [] for record in records: @@ -398,7 +357,7 @@ class EDIMixin(object): return [self.edi_m2o(cr, uid, r, context=context) for r in records] def edi_export(self, cr, uid, records, edi_struct=None, context=None): - """Returns a list of dicts representing an edi.document containing the + """Returns a list of dicts representing EDI documents containing the records, and matching the given ``edi_struct``, if provided. :param edi_struct: if provided, edi_struct should be a dictionary @@ -443,50 +402,6 @@ class EDIMixin(object): results.append(edi_dict) return results - def edi_export_and_email(self, cr, uid, ids, template_ext_id, context=None): - """Export the given records just like :meth:`~.export_edi`, the render the - given email template, in order to trigger appropriate notifications. - This method is intended to be called as part of business documents' - lifecycle, so it silently ignores any error occurring during the process, - as this is usually non-critical. To avoid any delay, it is also asynchronous - and will spawn a short-lived thread to perform the action. - - :param str template_ext_id: external id of the email.template to use for - the mail notifications - :return: True - """ - def email_task(): - db = pooler.get_db(cr.dbname) - local_cr = None - try: - time.sleep(3) # lame workaround to wait for commit of parent transaction - # grab a fresh browse_record on local cursor - local_cr = db.cursor() - web_root_url = self.pool.get('ir.config_parameter').get_param(local_cr, uid, 'web.base.url') - if not web_root_url: - _logger.warning('Ignoring EDI mail notification, web.base.url is not defined in parameters.') - return - mail_tmpl = self._edi_get_object_by_external_id(local_cr, uid, template_ext_id, 'email.template', context=context) - if not mail_tmpl: - # skip EDI export if the template was not found - _logger.warning('Ignoring EDI mail notification, template %s cannot be located.', template_ext_id) - return - for edi_record in self.browse(local_cr, uid, ids, context=context): - edi_token = self.pool.get('edi.document').export_edi(local_cr, uid, [edi_record], context = context)[0] - edi_context = dict(context, edi_web_url_view=EDI_VIEW_WEB_URL % (web_root_url, local_cr.dbname, edi_token)) - self.pool.get('email.template').send_mail(local_cr, uid, mail_tmpl.id, edi_record.id, - force_send=False, context=edi_context) - _logger.info('EDI export successful for %s #%s, email notification sent.', self._name, edi_record.id) - except Exception: - _logger.warning('Ignoring EDI mail notification, failed to generate it.', exc_info=True) - finally: - if local_cr: - local_cr.commit() - local_cr.close() - - threading.Thread(target=email_task, name='EDI ExportAndEmail for %s %r' % (self._name, ids)).start() - return True - def _edi_get_object_by_name(self, cr, uid, name, model_name, context=None): model = self.pool.get(model_name) search_results = model.name_search(cr, uid, name, operator='=', context=context) @@ -515,18 +430,20 @@ class EDIMixin(object): file_name = record.name_get()[0][1] file_name = re.sub(r'[^a-zA-Z0-9_-]', '_', file_name) file_name += ".pdf" - ir_attachment = self.pool.get('ir.attachment').create(cr, uid, - {'name': file_name, - 'datas': result, - 'datas_fname': file_name, - 'res_model': self._name, - 'res_id': record.id, - 'type': 'binary'}, - context=context) + self.pool.get('ir.attachment').create(cr, uid, + { + 'name': file_name, + 'datas': result, + 'datas_fname': file_name, + 'res_model': self._name, + 'res_id': record.id, + 'type': 'binary' + }, + context=context) - def _edi_import_attachments(self, cr, uid, record_id, edi_document, context=None): + def _edi_import_attachments(self, cr, uid, record_id, edi, context=None): ir_attachment = self.pool.get('ir.attachment') - for attachment in edi_document.get('__attachments', []): + for attachment in edi.get('__attachments', []): # check attachment data is non-empty and valid file_data = None try: @@ -614,19 +531,19 @@ class EDIMixin(object): self._edi_external_id(cr, uid, target, existing_id=ext_id_members['id'], existing_module=module, context=context) return target.id - def edi_import(self, cr, uid, edi_document, context=None): - """Imports a dict representing an edi.document into the system. + def edi_import(self, cr, uid, edi, context=None): + """Imports a dict representing an EDI document into the system. - :param dict edi_document: EDI document to import + :param dict edi: EDI document to import :return: the database ID of the imported record """ - assert self._name == edi_document.get('__import_model') or \ - ('__import_model' not in edi_document and self._name == edi_document.get('__model')), \ + assert self._name == edi.get('__import_model') or \ + ('__import_model' not in edi and self._name == edi.get('__model')), \ "EDI Document Model and current model do not match: '%s' (EDI) vs '%s' (current)." % \ - (edi_document['__model'], self._name) + (edi['__model'], self._name) # First check the record is now already known in the database, in which case it is ignored - ext_id_members = split_external_id(edi_document['__id']) + ext_id_members = split_external_id(edi['__id']) existing = self._edi_get_object_by_external_id(cr, uid, ext_id_members['full'], self._name, context=context) if existing: _logger.info("'%s' EDI Document with ID '%s' is already known, skipping import!", self._name, ext_id_members['full']) @@ -634,7 +551,7 @@ class EDIMixin(object): record_values = {} o2m_todo = {} # o2m values are processed after their parent already exists - for field_name, field_value in edi_document.iteritems(): + for field_name, field_value in edi.iteritems(): # skip metadata and empty fields if field_name.startswith('__') or field_value is None or field_value is False: continue @@ -679,7 +596,7 @@ class EDIMixin(object): dest_model.edi_import(cr, uid, o2m_line, context=context) # process the attachments, if any - self._edi_import_attachments(cr, uid, record_id, edi_document, context=context) + self._edi_import_attachments(cr, uid, record_id, edi, context=context) return record_id diff --git a/addons/edi/models/res_company.py b/addons/edi/models/res_company.py index 0ab6607cdad..d87827df99d 100644 --- a/addons/edi/models/res_company.py +++ b/addons/edi/models/res_company.py @@ -2,7 +2,7 @@ ############################################################################## # # OpenERP, Open Source Business Applications -# Copyright (c) 2011 OpenERP S.A.- You can view the ${object.state in ('draft', 'sent') and 'request for quotation' or 'order confirmation'} document and download it using the following link: -
- View Order -If you have any question, do not hesitate to contact us.
Thank you!
diff --git a/addons/purchase/test/process/edi_purchase_order.yml b/addons/purchase/test/process/edi_purchase_order.yml index d0e90f3788d..79f244a283d 100644 --- a/addons/purchase/test/process/edi_purchase_order.yml +++ b/addons/purchase/test/process/edi_purchase_order.yml @@ -27,16 +27,16 @@ - Then I export the purchase order via EDI - - !python {model: edi.document}: | - order_pool = self.pool.get('purchase.order') - order = order_pool.browse(cr, uid, ref("purchase_order_edi_1")) - token = self.export_edi(cr, uid, [order]) - assert token, 'Invalid EDI Token' - + !python {model: edi.edi}: | + import json + order_pool = self.pool.get('purchase.order') + order = order_pool.browse(cr, uid, ref("purchase_order_edi_1")) + edi_doc = self.generate_edi(cr, uid, [order]) + assert isinstance(json.loads(edi_doc)[0], dict), 'EDI doc should be a JSON dict' - Then I import a sample EDI document of a sale order - - !python {model: edi.document}: | + !python {model: edi.edi}: | purchase_order_pool = self.pool.get('purchase.order') edi_document = { "__id": "sale:724f93ec-ddd0-11e0-88ec-701a04e25543.sale_order_test", diff --git a/addons/sale/edi/sale_order.py b/addons/sale/edi/sale_order.py index edec15a86ae..0ca1b55898c 100644 --- a/addons/sale/edi/sale_order.py +++ b/addons/sale/edi/sale_order.py @@ -2,7 +2,7 @@ ############################################################################## # # OpenERP, Open Source Business Applications -# Copyright (c) 2011 OpenERP S.A.- You can view the ${object.state in ('draft', 'sent') and 'quotation' or 'order confirmation'} document, download it and pay online using the following link: -
- View Order - % if object.order_policy in ('prepaid','manual') and object.company_id.paypal_account and object.state not in ('draft', 'sent'): <% comp_name = quote(object.company_id.name) diff --git a/addons/sale/test/edi_sale_order.yml b/addons/sale/test/edi_sale_order.yml index 967aaa1337c..65a5887fe40 100644 --- a/addons/sale/test/edi_sale_order.yml +++ b/addons/sale/test/edi_sale_order.yml @@ -25,15 +25,16 @@ - Then I export the sale order via EDI - - !python {model: edi.document}: | + !python {model: edi.edi}: | + import json sale_order = self.pool.get('sale.order') so = sale_order.browse(cr, uid, ref("sale_order_edi_1")) - token = self.export_edi(cr, uid, [so]) - assert token, 'Invalid EDI Token' + edi_doc = self.generate_edi(cr, uid, [so]) + assert isinstance(json.loads(edi_doc)[0], dict), 'EDI doc should be a JSON dict' - Then I import a sample EDI document of a purchase order - - !python {model: edi.document}: | + !python {model: edi.edi}: | sale_order_pool = self.pool.get('sale.order') edi_document = { "__id": "purchase:5af1272e-dd26-11e0-b65e-701a04e25543.purchase_order_test",