[IMP] edi: work-in-progress: remove permanent storage of edi.document

The EDI documents will now be generated on demand
and available from the Portal view of each document.
Instead of getting a link to a statically generated
EDI document, customers will receive a link to
the portal access to the document. They will be able
to signup on the portal the first time as well,
provided they are using the secure token that was
sent to them (i.e. the right link).

The link to pay online will be available in the
portal as well.

Still much to do, this is a small first step,
with edi.document renamed to edi.edi for
consistency, as it will not persist any
edi.document anymore.

bzr revid: odo@openerp.com-20121011152008-bht7ub6woaex0a7u
This commit is contained in:
Olivier Dony 2012-10-11 17:20:08 +02:00
parent d1698d2b11
commit 5f24594223
21 changed files with 86 additions and 337 deletions

View File

@ -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 = {}

View File

@ -1,17 +1,6 @@
<?xml version="1.0" ?>
<openerp>
<data>
<!-- EDI Export + Send email Action -->
<record id="ir_actions_server_edi_invoice" model="ir.actions.server">
<field name="code">if (object.type in ('out_invoice', 'out_refund')) and not object.partner_id.opt_out: object.edi_export_and_email(template_ext_id='account.email_template_edi_invoice', context=context)</field>
<field eval="6" name="sequence"/>
<field name="state">code</field>
<field name="type">ir.actions.server</field>
<field name="model_id" ref="account.model_account_invoice"/>
<field name="condition">True</field>
<field name="name">Auto-email confirmed invoices</field>
</record>
<!-- EDI related Email Templates menu -->
<record model="ir.actions.act_window" id="action_email_templates">
<field name="name">Email Templates</field>
@ -27,17 +16,12 @@
</data>
<!-- Mail template and workflow bindings are done in a NOUPDATE block
<!-- Mail template are declared in a NOUPDATE block
so users can freely customize/delete them -->
<data noupdate="1">
<!-- bind the mailing server action to invoice open activity -->
<record id="account.act_open" model="workflow.activity">
<field name="action_id" ref="ir_actions_server_edi_invoice"/>
</record>
<!--Email template -->
<record id="email_template_edi_invoice" model="email.template">
<field name="name">Automated Invoice Notification Mail</field>
<field name="name">Invoice - Send by mail</field>
<field name="email_from">${object.user_id.email or object.company_id.email or 'noreply@localhost'}</field>
<field name="subject">${object.company_id.name} Invoice (Ref ${object.number or 'n/a' })</field>
<field name="email_to">${object.partner_id.email or ''}</field>
@ -61,12 +45,6 @@
&nbsp;&nbsp;Your contact: <a href="mailto:${object.user_id.email or ''}?subject=Invoice%20${object.number}">${object.user_id.name}</a>
</p>
<p>
You can view the invoice document, download it and pay online using the following link:
</p>
<a style="display:block; width: 150px; height:20px; margin-left: 120px; color: #FFF; font-family: 'Lucida Grande', Helvetica, Arial, sans-serif; font-size: 13px; font-weight: bold; text-align: center; text-decoration: none !important; line-height: 1; padding: 5px 0px 0px 0px; background-color: #8E0000; border-radius: 5px 5px; background-repeat: repeat no-repeat;"
href="${ctx.get('edi_web_url_view') or ''}">View Invoice</a>
% if object.company_id.paypal_account and object.type in ('out_invoice', 'in_refund'):
<%
comp_name = quote(object.company_id.name)

View File

@ -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
-

View File

@ -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

View File

@ -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,
}

View File

@ -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('<script type="text/javascript" src="%s"></script>'%i for i in webmain.manifest_list(req, mods, 'js'))
d["css"] = "\n".join('<link rel="stylesheet" href="%s">'%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:

View File

@ -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)

View File

@ -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

View File

@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2011 OpenERP S.A. <http://openerp.com>
# Copyright (c) 2011-2012 OpenERP S.A. <http://openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -19,7 +19,7 @@
#
##############################################################################
from osv import fields,osv
from openerp.osv import osv
class res_company(osv.osv):
"""Helper subclass for res.company providing util methods for working with

View File

@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2011 OpenERP S.A. <http://openerp.com>
# Copyright (c) 2011-2012 OpenERP S.A. <http://openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -19,7 +19,7 @@
#
##############################################################################
from osv import fields,osv
from openerp.osv import osv
from edi import EDIMixin
from openerp import SUPERUSER_ID

View File

@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2011 OpenERP S.A. <http://openerp.com>
# Copyright (c) 2011-2012 OpenERP S.A. <http://openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -20,10 +20,10 @@
##############################################################################
import logging
from osv import fields,osv
from openerp.osv import osv
from edi import EDIMixin
from openerp import SUPERUSER_ID
from tools.translate import _
from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
RES_PARTNER_EDI_STRUCT = {

View File

@ -1,3 +0,0 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_ir_edi_all_read,access_ir_edi_all_read,model_edi_document,,1,0,0,0
access_ir_edi_employee_create,access_ir_edi_employee_create,model_edi_document,base.group_user,1,0,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_ir_edi_all_read access_ir_edi_all_read model_edi_document 1 0 0 0
3 access_ir_edi_employee_create access_ir_edi_employee_create model_edi_document base.group_user 1 0 1 0

View File

@ -6,11 +6,10 @@
with an attached file, check the result, the alter the data
and reimport it.
-
!python {model: edi.document}: |
!python {model: edi.edi}: |
import json
partner_obj = self.pool.get('res.partner')
tokens = self.export_edi(cr, uid, [partner_obj.browse(cr, uid, ref('base.res_partner_2'))])
doc = self.get_document(cr, uid, tokens[0], context=context)
res_partner = self.pool.get('res.partner')
doc = self.generate_edi(cr, uid, [res_partner.browse(cr, uid, ref('base.res_partner_2'))])
edi_doc, = json.loads(doc)
# check content of the document
@ -36,8 +35,7 @@
"Expected (%r,> %r) after import 1, got %r" % ('res.partner', ref('base.res_partner_2'), result)
# export the same partner we just created, and see if the output matches the input
tokens = self.export_edi(cr, uid, [partner_obj.browse(cr, uid, result[1])])
doc_output = self.get_document(cr, uid, tokens[0], context=context)
doc_output = self.generate_edi(cr, uid, [res_partner.browse(cr, uid, result[1])])
edi_doc_output, = json.loads(doc_output)
for attribute in ('__model', '__module', '__id', 'name', '__attachments'):
assert edi_doc_output.get(attribute) == edi_doc.get(attribute), \

View File

@ -83,6 +83,7 @@ class mail_compose_message(osv.osv_memory):
'datas_fname': attach_fname,
'res_model': model,
'res_id': res_id,
'type': 'binary', # override default_type from context, possibly meant for another model!
}
values['attachment_ids'].append(ir_attach_obj.create(cr, uid, data_attach, context=context))
else:

View File

@ -60,7 +60,7 @@
<li>
<span t-if="(attachment.upload or attachment.percent_loaded&lt;100)" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}" t-attf-name="{attachment.name || attachment.filename}">
<div class="oe_upload_in_process">
<span>...wait upload...</span>
<span>Upload in progress...</span>
<div t-attf-style="{attachment.percent_loaded&gt;0?'':'display:none;'}"/>
<div t-attf-style="{attachment.percent_loaded&gt;20?'':'display:none;'}"/>
<div t-attf-style="{attachment.percent_loaded&gt;40?'':'display:none;'}"/>
@ -73,7 +73,7 @@
<t t-raw="attachment.name || attachment.filename"/>
</a>
<t t-if="widget.options.thread.show_attachment_delete and (!attachment.upload or attachment.percent_loaded&gt;=100)">
<a class="oe_right oe_mail_attachment_delete" title="Delete this attachmentt" t-attf-data-id="{attachment.id}">x</a>
<a class="oe_right oe_mail_attachment_delete" title="Delete this attachment" t-attf-data-id="{attachment.id}">x</a>
</t>
</li>

View File

@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2011 OpenERP S.A. <http://openerp.com>
# Copyright (c) 2011-2012 OpenERP S.A. <http://openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -19,13 +19,8 @@
#
##############################################################################
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from osv import fields, osv, orm
from openerp.osv import osv
from edi import EDIMixin
from edi.models import edi
from tools import DEFAULT_SERVER_DATE_FORMAT
from tools.translate import _
PURCHASE_ORDER_LINE_EDI_STRUCT = {
@ -62,16 +57,6 @@ PURCHASE_ORDER_EDI_STRUCT = {
class purchase_order(osv.osv, EDIMixin):
_inherit = 'purchase.order'
def wkf_send_rfq(self, cr, uid, ids, context=None):
""""Override this method to add a link to mail"""
if context is None:
context = {}
purchase_objs = self.browse(cr, uid, ids, context=context)
edi_token = self.pool.get('edi.document').export_edi(cr, uid, purchase_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(purchase_order, self).wkf_send_rfq(cr, uid, ids, context=ctx)
def edi_export(self, cr, uid, records, edi_struct=None, context=None):
"""Exports a purchase order"""
edi_struct = dict(edi_struct or PURCHASE_ORDER_EDI_STRUCT)
@ -109,7 +94,7 @@ class purchase_order(osv.osv, EDIMixin):
res_partner_obj = self.pool.get('res.partner')
# imported company_address = new partner address
src_company_id, src_company_name = edi_document.pop('company_id')
_, src_company_name = edi_document.pop('company_id')
address_info = edi_document.pop('company_address')
address_info['customer'] = True
if 'name' not in address_info:

View File

@ -1,18 +1,6 @@
<?xml version="1.0" ?>
<openerp>
<data>
<!--Export edi document -->
<!--
<record id="ir_actions_server_edi_purchase" model="ir.actions.server">
<field name="code">if not object.partner_id.opt_out: object.edi_export_and_email(template_ext_id='purchase.email_template_edi_purchase', context=context)</field>
<field name="state">code</field>
<field name="type">ir.actions.server</field>
<field name="model_id" ref="purchase.model_purchase_order"/>
<field name="condition">True</field>
<field name="name">Auto-email confirmed purchase orders</field>
</record>
-->
<!-- EDI related Email Templates menu -->
<record model="ir.actions.act_window" id="action_email_templates">
<field name="name">Email Templates</field>
@ -25,19 +13,12 @@
</record>
</data>
<!-- Mail template and workflow bindings are done in a NOUPDATE block
<!-- Mail template are declared in a NOUPDATE block
so users can freely customize/delete them -->
<data noupdate="1">
<!-- bind the mailing server action to purchase.order confirmed activity -->
<!--
<record id="purchase.act_confirmed" model="workflow.activity">
<field name="action_id" ref="ir_actions_server_edi_purchase"/>
</record>
-->
<!--Email template -->
<record id="email_template_edi_purchase" model="email.template">
<field name="name">Automated Purchase Order Notification Mail</field>
<field name="name">Purchase Order - Send by mail</field>
<field name="email_from">${object.validator.email or ''}</field>
<field name="subject">${object.company_id.name} Order (Ref ${object.name or 'n/a' })</field>
<field name="email_to">${object.partner_id.email}</field>
@ -64,12 +45,6 @@
&nbsp;&nbsp;Your contact: <a href="mailto:${object.validator.email or ''}?subject=Order%20${object.name}">${object.validator.name}</a>
</p>
<p>
You can view the ${object.state in ('draft', 'sent') and 'request for quotation' or 'order confirmation'} document and download it using the following link:
</p>
<a style="display:block; width: 150px; height:20px; margin-left: 120px; color: #FFF; font-family: 'Lucida Grande', Helvetica, Arial, sans-serif; font-size: 13px; font-weight: bold; text-align: center; text-decoration: none !important; line-height: 1; padding: 5px 0px 0px 0px; background-color: #8E0000; border-radius: 5px 5px; background-repeat: repeat no-repeat;"
href="${ctx.get('edi_web_url_view') or ''}">View Order</a>
<br/>
<p>If you have any question, do not hesitate to contact us.</p>
<p>Thank you!</p>

View File

@ -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",

View File

@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2011 OpenERP S.A. <http://openerp.com>
# Copyright (c) 2011-2012 OpenERP S.A. <http://openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -19,13 +19,8 @@
#
##############################################################################
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from osv import fields, osv, orm
from openerp.osv import osv
from edi import EDIMixin
from edi.models import edi
from tools import DEFAULT_SERVER_DATE_FORMAT
SALE_ORDER_LINE_EDI_STRUCT = {
'sequence': True,
@ -65,15 +60,6 @@ SALE_ORDER_EDI_STRUCT = {
class sale_order(osv.osv, EDIMixin):
_inherit = 'sale.order'
def action_quotation_send(self, cr, uid, ids, context=None):
if context is None:
context = {}
sale_objs = self.browse(cr, uid, ids, context=context)
edi_token = self.pool.get('edi.document').export_edi(cr, uid, sale_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(sale_order, self).action_quotation_send(cr, uid, ids, context=ctx)
def edi_export(self, cr, uid, records, edi_struct=None, context=None):
"""Exports a Sale order"""
edi_struct = dict(edi_struct or SALE_ORDER_EDI_STRUCT)
@ -112,7 +98,7 @@ class sale_order(osv.osv, EDIMixin):
res_partner_obj = self.pool.get('res.partner')
# imported company_address = new partner address
src_company_id, src_company_name = edi_document.pop('company_id')
_, src_company_name = edi_document.pop('company_id')
address_info = edi_document.pop('company_address')
address_info['supplier'] = True
@ -171,7 +157,6 @@ class sale_order(osv.osv, EDIMixin):
currency_id = res_currency.edi_import(cr, uid, currency_info, context=context)
order_currency = res_currency.browse(cr, uid, currency_id)
date_order = edi_document['date_order']
partner_ref = edi_document.pop('partner_ref', False)
edi_document['client_order_ref'] = edi_document['name']
edi_document['name'] = partner_ref or edi_document['name']
@ -185,7 +170,7 @@ class sale_order(osv.osv, EDIMixin):
order_lines = edi_document['order_line']
for order_line in order_lines:
self._edi_requires_attributes(( 'product_id', 'product_uom', 'product_qty', 'price_unit'), order_line)
self._edi_requires_attributes(('product_id', 'product_uom', 'product_qty', 'price_unit'), order_line)
order_line['product_uom_qty'] = order_line['product_qty']
del order_line['product_qty']

View File

@ -1,7 +1,6 @@
<?xml version="1.0" ?>
<openerp>
<data>
<!-- EDI related Email Templates menu -->
<record model="ir.actions.act_window" id="action_email_templates">
<field name="name">Email Templates</field>
@ -15,14 +14,13 @@
<menuitem id="base.menu_sales_configuration_misc" name="Miscellaneous" parent="base.menu_base_config" sequence="75"/>
</data>
<!-- Mail template is done in a NOUPDATE block
so users can freely customize/delete them -->
<data noupdate="1">
<!--Email template -->
<record id="email_template_edi_sale" model="email.template">
<field name="name">Automated Sale Order Notification Mail</field>
<field name="name">Sale Order - Send by mail</field>
<field name="email_from">${object.user_id.email or ''}</field>
<field name="subject">${object.company_id.name} Order (Ref ${object.name or 'n/a' })</field>
<field name="email_to">${object.partner_invoice_id.email}</field>
@ -49,12 +47,6 @@
&nbsp;&nbsp;Your contact: <a href="mailto:${object.user_id.email or ''}?subject=Order%20${object.name}">${object.user_id.name}</a>
</p>
<p>
You can view the ${object.state in ('draft', 'sent') and 'quotation' or 'order confirmation'} document, download it and pay online using the following link:
</p>
<a style="display:block; width: 150px; height:20px; margin-left: 120px; color: #FFF; font-family: 'Lucida Grande', Helvetica, Arial, sans-serif; font-size: 13px; font-weight: bold; text-align: center; text-decoration: none !important; line-height: 1; padding: 5px 0px 0px 0px; background-color: #8E0000; border-radius: 5px 5px; background-repeat: repeat no-repeat;"
href="${ctx.get('edi_web_url_view') or ''}">View Order</a>
% 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)

View File

@ -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",