[MERGE] from trunk

bzr revid: fva@openerp.com-20121121165851-fl8qq97ufx1ya5lu
This commit is contained in:
Frédéric van der Essen 2012-11-21 17:58:51 +01:00
commit a7751a5af2
89 changed files with 4125 additions and 1757 deletions

View File

@ -391,29 +391,34 @@ class account_invoice(osv.osv):
'''
This function opens a window to compose an email, with the edi invoice template message loaded by default
'''
mod_obj = self.pool.get('ir.model.data')
template = mod_obj.get_object_reference(cr, uid, 'account', 'email_template_edi_invoice')
template_id = template and template[1] or False
res = mod_obj.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')
res_id = res and res[1] or False
assert len(ids) == 1, 'This option should only be used for a single id at a time.'
ir_model_data = self.pool.get('ir.model.data')
try:
template_id = ir_model_data.get_object_reference(cr, uid, 'account', 'email_template_edi_invoice')[1]
except ValueError:
template_id = False
try:
compose_form_id = ir_model_data.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')[1]
except ValueError:
compose_form_id = False
ctx = dict(context)
ctx.update({
'default_model': 'account.invoice',
'default_res_id': ids[0],
'default_use_template': True,
'default_use_template': bool(template_id),
'default_template_id': template_id,
'default_composition_mode': 'comment',
'mark_invoice_as_sent': True,
})
return {
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
'res_model': 'mail.compose.message',
'views': [(res_id, 'form')],
'view_id': res_id,
'type': 'ir.actions.act_window',
'views': [(compose_form_id, 'form')],
'view_id': compose_form_id,
'target': 'new',
'context': ctx,
'nodestroy': True,
}
def confirm_paid(self, cr, uid, ids, context=None):
@ -1726,8 +1731,6 @@ class account_invoice_tax(osv.osv):
})
return res
account_invoice_tax()
class res_partner(osv.osv):
""" Inherits partner and adds invoice information in the partner form """
@ -1741,16 +1744,14 @@ class res_partner(osv.osv):
default.update({'invoice_ids' : []})
return super(res_partner, self).copy(cr, uid, id, default, context)
res_partner()
class mail_message(osv.osv):
_name = 'mail.message'
_inherit = 'mail.message'
class mail_compose_message(osv.osv):
_inherit = 'mail.compose.message'
def _postprocess_sent_message(self, cr, uid, message, context=None):
if message.model == 'account.invoice':
self.pool.get('account.invoice').write(cr, uid, [message.res_id], {'sent':True}, context=context)
return super(mail_message, self)._postprocess_sent_message(cr, uid, message=message, context=context)
def send_mail(self, cr, uid, ids, context=None):
context = context or {}
if context.get('default_model') == 'account.invoice' and context.get('default_res_id') and context.get('mark_invoice_as_sent'):
self.pool.get('account.invoice').write(cr, uid, [context['default_res_id']], {'sent': True}, context=context)
return super(mail_compose_message, self).send_mail(cr, uid, ids, context=context)
mail_message()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

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,9 +19,8 @@
#
##############################################################################
from osv import fields, osv, orm
from openerp.osv import osv
from edi import EDIMixin
from edi.models import edi
INVOICE_LINE_EDI_STRUCT = {
'name': True,
@ -71,16 +70,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)
@ -111,8 +100,8 @@ class account_invoice(osv.osv, EDIMixin):
return tax_account
def _edi_invoice_account(self, cr, uid, partner_id, invoice_type, context=None):
partner_pool = self.pool.get('res.partner')
partner = partner_pool.browse(cr, uid, partner_id, context=context)
res_partner = self.pool.get('res.partner')
partner = res_partner.browse(cr, uid, partner_id, context=context)
if invoice_type in ('out_invoice', 'out_refund'):
invoice_account = partner.property_account_receivable
else:
@ -136,31 +125,30 @@ 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')
xid, company_name = edi_document.pop('company_id')
# Retrofit address info into a unified partner info (changed in v7 - used to keep them separate)
company_address_edi = edi_document.pop('company_address')
company_address_edi['name'] = company_name
company_address_edi['is_company'] = True
company_address_edi['__import_model'] = 'res.partner'
company_address_edi['__id'] = xid # override address ID, as of v7 they should be the same anyway
if company_address_edi.get('logo'):
company_address_edi['image'] = company_address_edi.pop('logo')
invoice_type = edi_document['type']
partner_value = {}
if invoice_type in ('out_invoice', 'out_refund'):
partner_value.update({'customer': True})
if invoice_type in ('in_invoice', 'in_refund'):
partner_value.update({'supplier': True})
# imported company_address = new partner address
address_info = edi_document.pop('company_address')
if 'name' not in address_info:
address_info['name'] = src_company_name
address_info['type'] = 'invoice'
address_info.update(partner_value)
address_id = res_partner.edi_import(cr, uid, address_info, context=context)
if invoice_type.startswith('out_'):
company_address_edi['customer'] = True
else:
company_address_edi['supplier'] = True
partner_id = res_partner.edi_import(cr, uid, company_address_edi, context=context)
# modify edi_document to refer to new partner
partner_address = res_partner.browse(cr, uid, address_id, context=context)
address_edi_m2o = self.edi_m2o(cr, uid, partner_address, context=context)
edi_document['partner_id'] = address_edi_m2o
edi_document.pop('partner_address', False) # ignored
return address_id
partner = res_partner.browse(cr, uid, partner_id, context=context)
partner_edi_m2o = self.edi_m2o(cr, uid, partner, context=context)
edi_document['partner_id'] = partner_edi_m2o
edi_document.pop('partner_address', None) # ignored, that's supposed to be our own address!
return partner_id
def edi_import(self, cr, uid, edi_document, context=None):
""" During import, invoices will import the company that is provided in the invoice as
@ -200,7 +188,7 @@ class account_invoice(osv.osv, EDIMixin):
invoice_type = invoice_type.startswith('in_') and invoice_type.replace('in_','out_') or invoice_type.replace('out_','in_')
edi_document['type'] = invoice_type
#import company as a new partner
# import company as a new partner
partner_id = self._edi_import_company(cr, uid, edi_document, context=context)
# Set Account

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,28 +16,25 @@
</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 Email</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_recipients">${object.partner_id.id}</field>
<field name="model_id" ref="account.model_account_invoice"/>
<field name="auto_delete" eval="True"/>
<field name="report_template" ref="account_invoices"/>
<field name="report_name">Invoice_${(object.number or '').replace('/','_')}_${object.state == 'draft' and 'draft' or ''}</field>
<field name="body_html"><![CDATA[
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); ">
<p>Hello${object.partner_id.name and ' ' or ''}${object.partner_id.name or ''},</p>
<p>A new invoice is available for ${object.partner_id.name}: </p>
<p>A new invoice is available for you: </p>
<p style="border-left: 1px solid #8e0000; margin-left: 30px;">
&nbsp;&nbsp;<strong>REFERENCES</strong><br />
@ -58,21 +44,17 @@
% if object.origin:
&nbsp;&nbsp;Order reference: ${object.origin}<br />
% endif
% if object.user_id:
&nbsp;&nbsp;Your contact: <a href="mailto:${object.user_id.email or ''}?subject=Invoice%20${object.number}">${object.user_id.name}</a>
% endif
</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)
inv_number = quote(object.number)
paypal_account = quote(object.company_id.paypal_account)
inv_amount = quote(str(object.amount_total))
inv_amount = quote(str(object.residual))
cur_name = quote(object.currency_id.name)
paypal_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&amp;business=%s&amp;item_name=%s%%20Invoice%%20%s&amp;" \
"invoice=%s&amp;amount=%s&amp;currency_code=%s&amp;button_subtype=services&amp;no_note=1&amp;bn=OpenERP_Invoice_PayNow_%s" % \

View File

@ -227,7 +227,7 @@
</div>
</group>
<separator string="Bank &amp; Cash"/>
<group>
<group name="bank_cash">
<label for="id" string="Configuration"/>
<div>
<div>

View File

@ -38,45 +38,46 @@
-
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
Then I import a sample EDI document of another customer invoice from OpenERP 7.0
-
!python {model: account.invoice}: |
import time
edi_document = {
"__id": "account:b22acf7a-ddcd-11e0-a4db-701a04e25543.random_invoice_763jsms",
"__id": "account:b33adf8a-decd-11f0-a4de-702a04e25700.random_invoice_763jsms",
"__module": "account",
"__model": "account.invoice",
"__version": [6,1,0],
"internal_number": time.strftime("SAJ/%Y/002"),
"__version": [7,0,0],
"internal_number": time.strftime("SAJ/%Y/070"),
"company_address": {
"__id": "base:b22acf7a-ddcd-11e0-a4db-701a04e25543.main_address",
"__id": "base:b33adf8a-decd-11f0-a4de-702a04e25700.main_address",
"__module": "base",
"__model": "res.partner",
"city": "Gerompont",
"name": "Company main address",
"zip": "1367",
"country_id": ["base:b22acf7a-ddcd-11e0-a4db-701a04e25543.be", "Belgium"],
"country_id": ["base:b33adf8a-decd-11f0-a4de-702a04e25700.be", "Belgium"],
"phone": "(+32).81.81.37.00",
"street": "Chaussee de Namur 40",
"bank_ids": [
["base:b22acf7a-ddcd-11e0-a4db-701a04e25543.res_partner_bank-ZrTWzesfsdDJzGbp","Sample bank: 123465789-156113"]
["base:b33adf8a-decd-11f0-a4de-702a04e25700.res_partner_bank-ZrTWzesfsdDJzGbp","Sample bank: 70-123465789-156113"]
],
},
"company_id": ["account:b22acf7a-ddcd-11e0-a4db-701a04e25543.res_company_test11", "Thomson pvt. ltd."],
"company_id": ["account:b33adf8a-decd-11f0-a4de-702a04e25700.res_company_test11", "Thomson pvt. ltd."],
"currency": {
"__id": "base:b22acf7a-ddcd-11e0-a4db-701a04e25543.EUR",
"__id": "base:b33adf8a-decd-11f0-a4de-702a04e25700.EUR",
"__module": "base",
"__model": "res.currency",
"code": "EUR",
"symbol": "€",
},
"partner_id": ["account:b22acf7a-ddcd-11e0-a4db-701a04e25543.res_partner_test20", "Junjun wala"],
"partner_id": ["account:b33adf8a-decd-11f0-a4de-702a04e25700.res_partner_test20", "Junjun wala"],
"partner_address": {
"__id": "base:5af1272e-dd26-11e0-b65e-701a04e25543.res_partner_address_7wdsjasdjh",
"__module": "base",
@ -91,7 +92,7 @@
"date_invoice": time.strftime('%Y-%m-%d'),
"name": "sample invoice",
"tax_line": [{
"__id": "account:b22acf7a-ddcd-11e0-a4db-701a04e25543.account_invoice_tax-4g4EutbiEMVl",
"__id": "account:b33adf8a-decd-11f0-a4de-702a04e25700.account_invoice_tax-4g4EutbiEMVl",
"__module": "account",
"__model": "account.invoice.tax",
"amount": 1000.0,
@ -102,21 +103,21 @@
"invoice_line": [{
"__module": "account",
"__model": "account.invoice.line",
"__id": "account:b22acf7a-ddcd-11e0-a4db-701a04e25543.account_invoice_line-1RP3so",
"uos_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_uom_unit", "Unit"],
"__id": "account:b33adf8a-decd-11f0-a4de-702a04e25700.account_invoice_line-1RP3so",
"uos_id": ["product:b33adf8a-decd-11f0-a4de-702a04e25700.product_uom_unit", "Unit"],
"name": "PC Assemble SC234",
"price_unit": 10.0,
"product_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_product_3", "[PCSC234] PC Assemble SC234"],
"product_id": ["product:b33adf8a-decd-11f0-a4de-702a04e25700.product_product_3", "[PCSC234] PC Assemble SC234"],
"quantity": 1.0
},
{
"__module": "account",
"__model": "account.invoice.line",
"__id": "account:b22acf7a-ddcd-11e0-a4db-701a04e25543.account_invoice_line-u2XV5",
"uos_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_uom_unit", "Unit"],
"__id": "account:b33adf8a-decd-11f0-a4de-702a04e25700.account_invoice_line-u2XV5",
"uos_id": ["product:b33adf8a-decd-11f0-a4de-702a04e25700.product_uom_unit", "Unit"],
"name": "PC on Demand",
"price_unit": 100.0,
"product_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_product_5", "[PC-DEM] PC on Demand"],
"product_id": ["product:b33adf8a-decd-11f0-a4de-702a04e25700.product_product_5", "[PC-DEM] PC on Demand"],
"quantity": 5.0
}]
}
@ -125,12 +126,13 @@
invoice_new = self.browse(cr, uid, invoice_id)
# check bank info on partner
assert invoice_new.partner_id.supplier, "Imported partner should be a supplier, as we just imported the document as a supplier invoice"
assert len(invoice_new.partner_id.bank_ids) == 1, "Expected 1 bank entry related to partner"
bank_info = invoice_new.partner_id.bank_ids[0]
assert bank_info.acc_number == "Sample bank: 123465789-156113", 'Expected "Sample bank: 123465789-156113", got %s' % bank_info.acc_number
assert bank_info.acc_number == "Sample bank: 70-123465789-156113", 'Expected "Sample bank: 70-123465789-156113", got %s' % bank_info.acc_number
assert invoice_new.partner_id.supplier, 'Imported Partner is not marked as supplier'
assert invoice_new.reference == time.strftime("SAJ/%Y/002"), "internal number is not stored in reference"
assert invoice_new.reference == time.strftime("SAJ/%Y/070"), "internal number is not stored in reference"
assert invoice_new.reference_type == 'none', "reference type is not set to 'none'"
assert invoice_new.internal_number == False, "internal number is not reset"
assert invoice_new.journal_id.id, "journal id is not selected"
@ -152,3 +154,111 @@
for inv_tax in invoice_new.tax_line:
assert inv_tax.manual, "tax line not set to manual"
assert inv_tax.account_id, "missing tax line account"
-
Then I import a sample EDI document of another customer invoice from OpenERP 6.1 (to test backwards compatibility)
-
!python {model: account.invoice}: |
import time
edi_document = {
"__id": "account:b22acf7a-ddcd-11e0-a4db-701a04e25543.random_invoice_763jsms",
"__module": "account",
"__model": "account.invoice",
"__version": [6,1,0],
"internal_number": time.strftime("SAJ/%Y/061"),
"company_address": {
"__id": "base:b22acf7a-ddcd-11e0-a4db-701a04e25543.main_address",
"__module": "base",
"__model": "res.partner.address",
"city": "Gerompont",
"zip": "1367",
"country_id": ["base:b22acf7a-ddcd-11e0-a4db-701a04e25543.be", "Belgium"],
"phone": "(+32).81.81.37.00",
"street": "Chaussee de Namur 40",
"bank_ids": [
["base:b22acf7a-ddcd-11e0-a4db-701a04e25543.res_partner_bank-ZrTWzesfsdDJzGbp","Sample bank: 123465789-156113"]
],
},
"company_id": ["account:b22acf7a-ddcd-11e0-a4db-701a04e25543.res_company_test11", "Thomson pvt. ltd."],
"currency": {
"__id": "base:b22acf7a-ddcd-11e0-a4db-701a04e25543.EUR",
"__module": "base",
"__model": "res.currency",
"code": "EUR",
"symbol": "€",
},
"partner_id": ["account:b22acf7a-ddcd-11e0-a4db-701a04e25543.res_partner_test20", "Junjun wala"],
"partner_address": {
"__id": "base:5af1272e-dd26-11e0-b65e-701a04e25543.res_partner_address_7wdsjasdjh",
"__module": "base",
"__model": "res.partner.address",
"phone": "(+32).81.81.37.00",
"street": "Chaussee de Namur 40",
"city": "Gerompont",
"zip": "1367",
"country_id": ["base:5af1272e-dd26-11e0-b65e-701a04e25543.be", "Belgium"],
},
"date_invoice": time.strftime('%Y-%m-%d'),
"name": "sample invoice",
"tax_line": [{
"__id": "account:b22acf7a-ddcd-11e0-a4db-701a04e25543.account_invoice_tax-4g4EutbiEMVl",
"__module": "account",
"__model": "account.invoice.tax",
"amount": 1000.0,
"manual": True,
"name": "sale tax",
}],
"type": "out_invoice",
"invoice_line": [{
"__module": "account",
"__model": "account.invoice.line",
"__id": "account:b22acf7a-ddcd-11e0-a4db-701a04e25543.account_invoice_line-1RP3so",
"uos_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_uom_unit", "PCE"],
"name": "Basic PC",
"price_unit": 10.0,
"product_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_product_pc1", "[PC1] Basic PC"],
"quantity": 1.0
},
{
"__module": "account",
"__model": "account.invoice.line",
"__id": "account:b22acf7a-ddcd-11e0-a4db-701a04e25543.account_invoice_line-u2XV5",
"uos_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_uom_unit", "PCE"],
"name": "Medium PC",
"price_unit": 100.0,
"product_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_product_pc3", "[PC3] Medium PC"],
"quantity": 5.0
}]
}
invoice_id = self.edi_import(cr, uid, edi_document, context=context)
assert invoice_id, 'EDI import failed'
invoice_new = self.browse(cr, uid, invoice_id)
# check bank info on partner
assert invoice_new.partner_id.supplier, "Imported partner should be a supplier, as we just imported the document as a supplier invoice"
assert len(invoice_new.partner_id.bank_ids) == 1, "Expected 1 bank entry related to partner"
bank_info = invoice_new.partner_id.bank_ids[0]
assert bank_info.acc_number == "Sample bank: 123465789-156113", 'Expected "Sample bank: 123465789-156113", got %s' % bank_info.acc_number
assert invoice_new.partner_id.supplier, 'Imported Partner is not marked as supplier'
assert invoice_new.reference == time.strftime("SAJ/%Y/061"), "internal number is not stored in reference"
assert invoice_new.reference_type == 'none', "reference type is not set to 'none'"
assert invoice_new.internal_number == False, "internal number is not reset"
assert invoice_new.journal_id.id, "journal id is not selected"
assert invoice_new.type == 'in_invoice', "Invoice type was not set properly"
assert len(invoice_new.invoice_line) == 2, "invoice lines are not same"
for inv_line in invoice_new.invoice_line:
if inv_line.name == 'Basic PC':
assert inv_line.uos_id.name == "PCE" , "uom is not same"
assert inv_line.price_unit == 10 , "price unit is not same"
assert inv_line.quantity == 1 , "product qty is not same"
assert inv_line.price_subtotal == 10, "price sub total is not same"
elif inv_line.name == 'Medium PC':
assert inv_line.uos_id.name == "PCE" , "uom is not same"
assert inv_line.price_unit == 100 , "price unit is not same"
assert inv_line.quantity == 5 , "product qty is not same"
assert inv_line.price_subtotal == 500, "price sub total is not same"
else:
raise AssertionError('unknown invoice line: %s' % inv_line)
for inv_tax in invoice_new.tax_line:
assert inv_tax.manual, "tax line not set to manual"
assert inv_tax.account_id, "missing tax line account"

View File

@ -0,0 +1,79 @@
# Macedonian translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:35+0000\n"
"PO-Revision-Date: 2012-11-20 17:57+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Macedonian <mk@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-11-21 04:48+0000\n"
"X-Generator: Launchpad (build 16293)\n"
#. module: analytic_user_function
#: field:analytic.user.funct.grid,product_id:0
msgid "Product"
msgstr "Производ"
#. module: analytic_user_function
#: model:ir.model,name:analytic_user_function.model_analytic_user_funct_grid
msgid "Relation table between users and products on a analytic account"
msgstr ""
#. module: analytic_user_function
#: constraint:hr.analytic.timesheet:0
msgid "You cannot modify an entry in a Confirmed/Done timesheet !."
msgstr ""
#. module: analytic_user_function
#: field:analytic.user.funct.grid,account_id:0
#: model:ir.model,name:analytic_user_function.model_account_analytic_account
msgid "Analytic Account"
msgstr "Аналитичко конто"
#. module: analytic_user_function
#: view:account.analytic.account:0
#: field:account.analytic.account,user_product_ids:0
msgid "Users/Products Rel."
msgstr ""
#. module: analytic_user_function
#: field:analytic.user.funct.grid,user_id:0
msgid "User"
msgstr "Корисник"
#. module: analytic_user_function
#: code:addons/analytic_user_function/analytic_user_function.py:96
#: code:addons/analytic_user_function/analytic_user_function.py:131
#, python-format
msgid "There is no expense account define for this product: \"%s\" (id:%d)"
msgstr ""
#. module: analytic_user_function
#: code:addons/analytic_user_function/analytic_user_function.py:95
#: code:addons/analytic_user_function/analytic_user_function.py:130
#, python-format
msgid "Error !"
msgstr "Грешка !"
#. module: analytic_user_function
#: constraint:account.analytic.account:0
msgid "Error! You can not create recursive analytic accounts."
msgstr ""
#. module: analytic_user_function
#: model:ir.model,name:analytic_user_function.model_hr_analytic_timesheet
msgid "Timesheet Line"
msgstr ""
#. module: analytic_user_function
#: view:analytic.user.funct.grid:0
msgid "User's Product for this Analytic Account"
msgstr ""

View File

@ -25,10 +25,10 @@
'version': '1.0',
'category': 'Tools',
'description': """
Allow users to login through Google OAuth2.
===========================================
Allow users to login through OAuth2 Provider.
=============================================
""",
'author': 'Victor Tabuenca',
'author': 'OpenERP s.a.',
'maintainer': 'OpenERP s.a.',
'website': 'http://www.openerp.com',
'depends': ['base', 'web', 'base_setup'],

View File

@ -39,6 +39,8 @@
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="menu_oauth_providers" parent="base.menu_users" name="OAuth Providers" action="action_oauth_provider" sequence="30"/>
<menuitem id="menu_oauth_providers" name="OAuth Providers"
parent="base.menu_users" sequence="30"
action="action_oauth_provider" groups="base.group_no_one"/>
</data>
</openerp>

View File

@ -34,7 +34,6 @@ class Controller(openerp.addons.web.http.Controller):
def retrieve(self, req, dbname, token):
""" retrieve the user info (name, login or email) corresponding to a signup token """
registry = RegistryManager.get(dbname)
user_info = None
with registry.cursor() as cr:
res_partner = registry.get('res.partner')
user_info = res_partner.signup_retrieve_info(cr, openerp.SUPERUSER_ID, token)

View File

@ -23,9 +23,7 @@ import time
import urllib
import urlparse
import openerp
from openerp.osv import osv, fields
from openerp import SUPERUSER_ID
from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools.safe_eval import safe_eval
@ -35,7 +33,7 @@ class SignupError(Exception):
def random_token():
# the token has an entropy of about 120 bits (6 bits/char * 20 chars)
chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
return ''.join(random.choice(chars) for i in xrange(20))
return ''.join(random.choice(chars) for _ in xrange(20))
def now():
return time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
@ -51,29 +49,44 @@ class res_partner(osv.Model):
(not partner.signup_expiration or dt <= partner.signup_expiration)
return res
def _get_signup_url(self, cr, uid, ids, name, arg, context=None):
""" determine a signup url for a given partner """
base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
# if required, make sure that every partner without user has a valid signup token
if context and context.get('signup_valid'):
unsigned_ids = [p.id for p in self.browse(cr, uid, ids, context) if not p.user_ids]
self.signup_prepare(cr, uid, unsigned_ids, context=context)
def _get_signup_url_for_action(self, cr, uid, ids, action='login', view_type=None, menu_id=None, res_id=None, context=None):
""" generate a signup url for the given partner ids and action, possibly overriding
the url state components (menu_id, id, view_type) """
res = dict.fromkeys(ids, False)
base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
for partner in self.browse(cr, uid, ids, context):
# when required, make sure the partner has a valid signup token
if context and context.get('signup_valid') and not partner.user_ids:
self.signup_prepare(cr, uid, [partner.id], context=context)
action_template = None
params = {
'action': urllib.quote(action),
'db': urllib.quote(cr.dbname),
}
if partner.signup_token:
params = (urllib.quote(cr.dbname), urllib.quote(partner.signup_token))
res[partner.id] = urlparse.urljoin(base_url, "#action=login&db=%s&token=%s" % params)
action_template = "?db=%(db)s#action=%(action)s&token=%(token)s"
params['token'] = urllib.quote(partner.signup_token)
elif partner.user_ids:
user = partner.user_ids[0]
params = (urllib.quote(cr.dbname), urllib.quote(user.login))
res[partner.id] = urlparse.urljoin(base_url, "#action=login&db=%s&login=%s" % params)
action_template = "?db=%(db)s#action=%(action)s&db=%(db)s&login=%(login)s"
params['login'] = urllib.quote(partner.user_ids[0].login)
if action_template:
if view_type:
action_template += '&view_type=%s' % urllib.quote(view_type)
if menu_id:
action_template += '&menu_id=%s' % urllib.quote(str(menu_id))
if res_id:
action_template += '&id=%s' % urllib.quote(str(res_id))
res[partner.id] = urlparse.urljoin(base_url, action_template % params)
return res
def _get_signup_url(self, cr, uid, ids, name, arg, context=None):
""" proxy for function field towards actual implementation """
return self._get_signup_url_for_action(cr, uid, ids, context=context)
_columns = {
'signup_token': fields.char(size=24, string='Signup Token'),
'signup_expiration': fields.datetime(string='Signup Expiration'),
'signup_token': fields.char('Signup Token'),
'signup_expiration': fields.datetime('Signup Expiration'),
'signup_valid': fields.function(_get_signup_valid, type='boolean', string='Signup Token is Valid'),
'signup_url': fields.function(_get_signup_url, type='char', string='Signup URL'),
}

View File

@ -16,7 +16,7 @@
<group col="4">
<field name="name"/>
<field name="model_id"/>
<field name="filter_id" domain="[('model_id','=',model)]" context="{'default_model_id': model}"/>
<field name="filter_id" domain="[('model_id','=',model), ('user_id', '=', False)]" context="{'default_model_id': model}"/>
<field name="sequence"/>
<field name="active"/>
<field name="model" invisible="1"/>

View File

@ -99,7 +99,6 @@ Dashboard for CRM will include:
'board_crm_view.xml',
'res_config_view.xml',
],
'demo': [
'crm_demo.xml',

View File

@ -46,6 +46,7 @@ Thanks,
<field name="name">Filter on leads which are draft.</field>
<field name="model_id">crm.lead</field>
<field name="domain">[('state','=','draft')]</field>
<field name="user_id" eval="False"/>
</record>
<!-- automated actions -->
@ -64,6 +65,7 @@ Thanks,
<field name="name">Filter on leads which come from USA.</field>
<field name="model_id">crm.lead</field>
<field name="domain">[('country_id','=','United States')]</field>
<field name="user_id" eval="False"/>
</record>
<record id="rule_set_followers_lead" model="base.action.rule">

View File

@ -832,7 +832,7 @@ class crm_lead(base_stage, format_address, osv.osv):
}
for line in msg.get('body', '').split('\n'):
line = line.strip()
res = tools.misc.command_re.match(line)
res = tools.command_re.match(line)
if res and maps.get(res.group(1).lower()):
key = maps.get(res.group(1).lower())
update_vals[key] = res.group(2).lower()
@ -846,7 +846,7 @@ class crm_lead(base_stage, format_address, osv.osv):
def stage_set_send_note(self, cr, uid, ids, stage_id, context=None):
""" Override of the (void) default notification method. """
stage_name = self.pool.get('crm.case.stage').name_get(cr, uid, [stage_id], context=context)[0][1]
return self.message_post(cr, uid, ids, body= _("Stage changed to <b>%s</b>.") % (stage_name), context=context)
return self.message_post(cr, uid, ids, body=_("Stage changed to <b>%s</b>.") % (stage_name), subtype="mt_crm_stage", context=context)
def case_get_note_msg_prefix(self, cr, uid, lead, context=None):
if isinstance(lead, (int, long)):
@ -855,17 +855,17 @@ class crm_lead(base_stage, format_address, osv.osv):
def create_send_note(self, cr, uid, ids, context=None):
for id in ids:
message = _("%s has been <b>created</b>.")% (self.case_get_note_msg_prefix(cr, uid, id, context=context))
message = _("%s has been <b>created</b>.") % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_post(cr, uid, [id], body=message, context=context)
return True
def case_mark_lost_send_note(self, cr, uid, ids, context=None):
message = _("Opportunity has been <b>lost</b>.")
return self.message_post(cr, uid, ids, body=message, context=context)
return self.message_post(cr, uid, ids, body=message, subtype="mt_crm_lost", context=context)
def case_mark_won_send_note(self, cr, uid, ids, context=None):
message = _("Opportunity has been <b>won</b>.")
return self.message_post(cr, uid, ids, body=message, context=context)
return self.message_post(cr, uid, ids, body=message, subtype="mt_crm_won", context=context)
def schedule_phonecall_send_note(self, cr, uid, ids, phonecall_id, action, context=None):
phonecall = self.pool.get('crm.phonecall').browse(cr, uid, [phonecall_id], context=context)[0]

View File

@ -154,5 +154,21 @@
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
</record>
<!-- mail subtype -->
<record id="mail.mt_crm_won" model="mail.message.subtype">
<field name="name">Won</field>
<field name="res_model">crm.lead</field>
</record>
<record id="mail.mt_crm_lost" model="mail.message.subtype">
<field name="name">Lost</field>
<field name="res_model">crm.lead</field>
<field name="default" eval="False"/>
</record>
<record id="mail.mt_crm_stage" model="mail.message.subtype">
<field name="name">Stage Changed</field>
<field name="res_model">crm.lead</field>
<field name="default" eval="False"/>
</record>
</data>
</openerp>

View File

@ -324,12 +324,14 @@ Andrew</field>
<record id="crm_case_15" model="crm.lead">
<field name="type">opportunity</field>
<field name="name">Plan to buy a Laptop</field>
<field eval="2570" name="planned_revenue"/>
<field name="name">Plan to buy RedHat servers</field>
<field eval="35000" name="planned_revenue"/>
<field eval="30.0" name="probability"/>
<field name="street">12 rue Albert Einstein</field>
<field name="country_id" ref="base.tr"/>
<field name="city">Istanbul</field>
<field name="street">69 rue de Chimay</field>
<field name="country_id" ref="base.be"/>
<field name="city">Wavre</field>
<field name="email_from">virginie@agrolait.com</field>
<field name="partner_id" ref="base.res_partner_2"/>
<field name="type_id" ref="type_lead8"/>
<field name="categ_ids" eval="[(6, 0, [categ_oppor1])]"/>
<field name="priority">1</field>
@ -337,7 +339,7 @@ Andrew</field>
<field eval="time.strftime('%Y-%m-10')" name="date_action"/>
<field name="title_action">Call to ask system requirement</field>
<field name="section_id" ref="crm_case_section_3"/>
<field name="user_id" ref="base.user_root"/>
<field name="user_id" ref="base.user_demo"/>
<field name="stage_id" ref="crm.stage_lead4"/>
<field eval="1" name="active"/>
</record>
@ -372,6 +374,8 @@ Andrew</field>
<field eval="100" name="planned_revenue"/>
<field eval="35.0" name="probability"/>
<field name="contact_name">Oliver Passot</field>
<field name="email_from">olivier.passo@balmer.inc.sa</field>
<field name="phone">+32 469 12 45 78</field>
<field name="partner_name">BalmerInc S.A.</field>
<field name="street">Rue des Palais 51, bte 33</field>
<field name="country_id" ref="base.be"/>
@ -391,14 +395,16 @@ Andrew</field>
<record id="crm_case_18" model="crm.lead">
<field name="type">opportunity</field>
<field name="name">Student's training plan in your Organization</field>
<field name="name">Trainee's training plan in your Organization</field>
<field eval="35000" name="planned_revenue"/>
<field eval="25.0" name="probability"/>
<field name="contact_name">Leland Martinez</field>
<field name="partner_name">Toronto University</field>
<field name="street">2488 Queens Bay</field>
<field name="country_id" ref="base.ca"/>
<field name="city">Toronto</field>
<field name="email_from">info@deltapc.com</field>
<field name="partner_name">Delta PC</field>
<field name="city">Fremont</field>
<field name="street">3661 Station Street</field>
<field name="country_id" ref="base.us"/>
<field name="partner_id" ref="base.res_partner_4"/>
<field name="type_id" ref="type_lead8"/>
<field name="categ_ids" eval="[(6, 0, [categ_oppor4,categ_oppor6])]"/>
<field name="priority">4</field>
@ -406,7 +412,7 @@ Andrew</field>
<field eval="time.strftime('%Y-%m-4')" name="date_action"/>
<field name="title_action">Call to define real needs about training</field>
<field name="section_id" ref="crm_case_section_2"/>
<field name="user_id" ref="base.user_root"/>
<field name="user_id" ref="base.user_demo"/>
<field name="stage_id" ref="crm.stage_lead3"/>
<field eval="1" name="active"/>
</record>
@ -542,77 +548,127 @@ Andrew</field>
<field eval="1" name="active"/>
</record>
<!-- Some messages linked to the previous opportunity -->
<record id="message_email0" model="mail.message">
<field name="subject">Plan to buy a Laptop</field>
<!-- Unsubscribe Admin from case15, subscribe Demo -->
<record id="crm_case_15" model="crm.lead">
<field name="message_follower_ids" eval="[(3, ref('base.partner_root')), (4, ref('base.partner_demo'))]"/>
</record>
<record id="crm_case_17" model="crm.lead">
<field name="message_follower_ids" eval="[(3, ref('base.partner_root')), (4, ref('base.partner_demo'))]"/>
</record>
<record id="crm_case_18" model="crm.lead">
<field name="message_follower_ids" eval="[(4, ref('base.partner_demo'))]"/>
</record>
<!-- Some messages linked to the previous opportunities -->
<record id="msg_case15_attach1" model="ir.attachment">
<field name="datas">bWlncmF0aW9uIHRlc3Q=</field>
<field name="datas_fname">YourCompany2012.doc</field>
<field name="name">YourCompany2012.doc</field>
</record>
<record id="msg_case15_1" model="mail.message">
<field name="subject">Plan to buy RedHat servers</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="body">&lt;![CDATA[Email0 inquiry]]&gt;&lt;div&gt;&lt;font size="2"&gt;Hello,&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font size="2"&gt;&lt;br&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font size="2"&gt;I am interested in your company's product and I plan to buy a new laptop having latest technologies and affordable price.&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font size="2"&gt;Can you please send me product catalogue?&lt;/font&gt;&lt;/div&gt;</field>
<field name="body"><![CDATA[<div>
<p>Hello,</p>
<p>I am interested in your company's products and I plan to buy a new laptop having latest technologies as well as an affordable price.</p>
<p>Could you please send me the product catalog?</p>]]></field>
<field name="type">email</field>
<field name="subtype_id" ref="mail.mt_comment"/>
<field name="email_from">virginie@agrolait.fr</field>
<field name="author_id" eval="False"/>
</record>
<record id="message_note0" model="mail.message">
<field name="subject">Re: Plan to buy a Laptop</field>
<record id="msg_case15_1_1" model="mail.message">
<field name="subject">Re: Plan to buy RedHat servers</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="type">comment</field>
<field name="body">Dear Customer,
Thanks for showing interest in our products.
We have attached the catalogue,
We would like to know your interests, so let us know when we can call you for more details.
Regards</field>
<field name="parent_id" ref="message_email0"/>
<field name="author_id" ref="base.partner_root"/>
<field name="subtype_id" ref="mail.mt_comment"/>
<field name="body"><![CDATA[<p>Dear customer,<br/>
Thanks for showing interest in our products! As requested, I send to you our products catalog.<br />
To be able to finely tune the solution, we would like to know precise needs. This way we wil be able to help you choosing the right infrastructure according to your requirements.<br/>
Best regards,</p>]]></field>
<field name="parent_id" ref="msg_case15_1"/>
<field name="author_id" ref="base.partner_demo"/>
<field name="attachment_ids" eval="[(6, 0, [ref('msg_case15_attach1')])]"/>
</record>
<record id="message_note0_comment0" model="mail.message">
<field name="subject">Re: Plan to buy a Laptop</field>
<record id="msg_case15_1_2" model="mail.message">
<field name="subject">Re: Plan to buy RedHat servers</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="type">comment</field>
<field name="body">&lt;div&gt;Thanks for the information,&lt;/div&gt;&lt;div&gt;I will visit the store soon.&lt;/div&gt;</field>
<field name="parent_id" ref="message_note0"/>
<field name="body"><![CDATA[<p>Thanks for the information!<br />I asked a precise specification to our technical expert. Here is what we precisely need:</p>
<ul>
<li>weekly backups, every Monday</li>
<li>backup time is not a blocking point for us, as we are closed all Monday, leaving time enough to perform the backup</li>
<li>reliability is very important; we need redundant servers and rollback capacity</li>
<li>a total capacity of about 2 TB</li>
</ul>
<p>Best regards,</p>]]></field>
<field name="type">email</field>
<field name="subtype_id" ref="mail.mt_comment"/>
<field name="parent_id" ref="msg_case15_1"/>
<field name="email_from">virginie@agrolait.fr</field>
<field name="author_id" eval="False"/>
</record>
<record id="msg_case15_1_3" model="mail.message">
<field name="subject">Re: Plan to buy RedHat servers</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="body"><![CDATA[<p>Hello</p>
<p>After our phonecall and discussion with our technical experts, here is the offer of YourCompany. We believe it will meet every requirement you had in mind. Please feel free to contact me for any detail or technical detail that is not clear enough for you.</p>
<p>Notice that as agreed on phone, we offer you a <b>10% discount on the hardware</b>!</p>
<p>Best regards,</p>]]></field>
<field name="type">email</field>
<field name="parent_id" ref="msg_case15_1"/>
<field name="author_id" ref="base.partner_demo"/>
</record>
<record id="message_note0_comment1" model="mail.message">
<field name="subject">Re: Plan to buy a Laptop</field>
<record id="msg_case17_1" model="mail.message">
<field name="subject">Catalog to send</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="res_id" ref="crm_case_17"/>
<field name="body"><![CDATA[<p>They just want pricing information about our services. I think sending our catalog should be sufficient.</p>]]></field>
<field name="type">comment</field>
<field name="body">&lt;font color="#1f1f1f"&gt;Can you tell me if the store is open at 9:00 PM?&lt;/b&gt;&lt;/font&gt;</field>
<field name="parent_id" ref="message_note0"/>
<field name="subtype_id" ref="mail.mt_comment"/>
<field name="author_id" ref="base.partner_demo"/>
</record>
<record id="message_email1" model="mail.message">
<field name="subject">Re: Plan to buy a Laptop</field>
<record id="msg_case18_1" model="mail.message">
<field name="subject">Inquiry</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="body">Yes, its open till 10:00 PM, you are welcome!</field>
<field name="type">email</field>
<field name="author_id" ref="base.partner_root"/>
<field name="res_id" ref="crm_case_18"/>
<field name="body"><![CDATA[<p>Hello!<br />
I am Leland Martinez, from the Delta PC. Maybe you remember, we talked a bit last month at this international conference.<br />
We would like to attend a training, but we are not quite sure about what we can ask. Maybe we should meet and talk about that?<br />
Best regards,</p>]]></field>
<field name="type">comment</field>
<field name="subtype_id" ref="mail.mt_comment"/>
<field name="author_id" ref="base.res_partner_4"/>
<field name="favorite_user_ids" eval="[(6, 0, [ref('base.user_root')])]"/>
</record>
<record id="message_email_12" model="mail.message">
<record id="msg_case18_2" model="mail.message">
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_18"/>
<field name="body"><![CDATA[<p>It seems very interesting. As you say, first of all we will have to define precisely what the training will be about, and your precise needs.</p>]]></field>
<field name="type">comment</field>
<field name="subtype_id" ref="mail.mt_comment"/>
<field name="parent_id" ref="msg_case18_1"/>
<field name="author_id" ref="base.partner_demo"/>
<field name="favorite_user_ids" eval="[(6, 0, [ref('base.user_root')])]"/>
</record>
<record id="msg_case1_1" model="mail.message">
<field name="subject">Inquiry</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_1"/>
<field name="body">Hello,
I am Jason from Le Club SARL.
I am intertested to attend a training organized in your company.
Can you send me the details ?</field>
<field name="body"><![CDATA[<p>Hello,<br />
I am Jason from Le Club SARL. I am interested to attend a training organized in your company.<br />
Can you send me the details ?</p>]]></field>
<field name="type">email</field>
</record>
<record id="message_email_13" model="mail.message">
<record id="msg_case2_1" model="mail.message">
<field name="subject">Need Details</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_2"/>
<field name="body">Want to know features and benifits to use the new software.</field>
<field name="body">Want to know features and benefits to use the new software.</field>
<field name="type">comment</field>
</record>
<!-- Call Function to set the opportunities as Unread -->
<function model="crm.lead" name="message_mark_as_unread"
eval="[ ref('crm_case_15'), ref('crm_case_16'),
ref('crm_case_23'), ref('crm_case_19')], {}"
/>
</data>
</openerp>

View File

@ -224,7 +224,7 @@ class crm_claim(base_stage, osv.osv):
}
for line in msg['body'].split('\n'):
line = line.strip()
res = tools.misc.command_re.match(line)
res = tools.command_re.match(line)
if res and maps.get(res.group(1).lower()):
key = maps.get(res.group(1).lower())
update_vals[key] = res.group(2).lower()

795
addons/crm_claim/i18n/nb.po Normal file
View File

@ -0,0 +1,795 @@
# Norwegian Bokmal translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
"PO-Revision-Date: 2012-11-20 12:52+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Norwegian Bokmal <nb@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-11-21 04:48+0000\n"
"X-Generator: Launchpad (build 16293)\n"
#. module: crm_claim
#: field:crm.claim.report,nbr:0
msgid "# of Cases"
msgstr "# av Saker."
#. module: crm_claim
#: view:crm.claim:0
#: view:crm.claim.report:0
msgid "Group By..."
msgstr "Grupper etter ..."
#. module: crm_claim
#: view:crm.claim:0
msgid "Responsibilities"
msgstr "Ansvaret."
#. module: crm_claim
#: field:crm.claim,date_action_next:0
msgid "Next Action Date"
msgstr "Neste aksjonsdato"
#. module: crm_claim
#: selection:crm.claim.report,month:0
msgid "March"
msgstr "Mars"
#. module: crm_claim
#: field:crm.claim.report,delay_close:0
msgid "Delay to close"
msgstr "Forsinket mht lukking"
#. module: crm_claim
#: field:crm.claim,resolution:0
msgid "Resolution"
msgstr "Oppløsning"
#. module: crm_claim
#: field:crm.claim,company_id:0
#: view:crm.claim.report:0
#: field:crm.claim.report,company_id:0
msgid "Company"
msgstr "Firma"
#. module: crm_claim
#: field:crm.claim,email_cc:0
msgid "Watchers Emails"
msgstr ""
#. module: crm_claim
#: view:crm.claim.report:0
msgid "#Claim"
msgstr "#Krav"
#. module: crm_claim
#: model:ir.actions.act_window,help:crm_claim.crm_claim_stage_act
msgid ""
"You can create claim stages to categorize the status of every claim entered "
"in the system. The stages define all the steps required for the resolution "
"of a claim."
msgstr ""
#. module: crm_claim
#: code:addons/crm_claim/crm_claim.py:132
#, python-format
msgid "The claim '%s' has been opened."
msgstr "Kravet '% s' har blitt åpnet."
#. module: crm_claim
#: view:crm.claim:0
msgid "Date Closed"
msgstr "Dato lukket"
#. module: crm_claim
#: view:crm.claim.report:0
#: field:crm.claim.report,day:0
msgid "Day"
msgstr "Dag"
#. module: crm_claim
#: view:crm.claim:0
msgid "Add Internal Note"
msgstr "Legg til internt notat."
#. module: crm_claim
#: help:crm.claim,section_id:0
msgid ""
"Sales team to which Case belongs to.Define Responsible user and Email "
"account for mail gateway."
msgstr ""
#. module: crm_claim
#: view:crm.claim:0
msgid "Claim Description"
msgstr "Hevder Beskrivelse."
#. module: crm_claim
#: field:crm.claim,message_ids:0
msgid "Messages"
msgstr "Meldinger"
#. module: crm_claim
#: model:crm.case.categ,name:crm_claim.categ_claim1
msgid "Factual Claims"
msgstr ""
#. module: crm_claim
#: selection:crm.claim,state:0
#: selection:crm.claim.report,state:0
msgid "Cancelled"
msgstr "Kansellert"
#. module: crm_claim
#: model:crm.case.resource.type,name:crm_claim.type_claim2
msgid "Preventive"
msgstr "Forebyggende"
#. module: crm_claim
#: field:crm.claim.report,date_closed:0
msgid "Close Date"
msgstr "Lukkingsdato"
#. module: crm_claim
#: field:crm.claim,ref:0
msgid "Reference"
msgstr "Referanse"
#. module: crm_claim
#: view:crm.claim.report:0
msgid "Date of claim"
msgstr "Dato av krav."
#. module: crm_claim
#: view:crm.claim:0
msgid "All pending Claims"
msgstr "Alle utestående krav."
#. module: crm_claim
#: view:crm.claim.report:0
msgid "# Mails"
msgstr "# E-poster"
#. module: crm_claim
#: view:crm.claim:0
msgid "Reset to Draft"
msgstr "Sett tilbake til utkast"
#. module: crm_claim
#: view:crm.claim:0
#: field:crm.claim,date_deadline:0
#: field:crm.claim.report,date_deadline:0
msgid "Deadline"
msgstr "Frist"
#. module: crm_claim
#: view:crm.claim:0
#: field:crm.claim,partner_id:0
#: view:crm.claim.report:0
#: field:crm.claim.report,partner_id:0
#: model:ir.model,name:crm_claim.model_res_partner
msgid "Partner"
msgstr "Partner"
#. module: crm_claim
#: view:crm.claim.report:0
msgid "Month of claim"
msgstr ""
#. module: crm_claim
#: selection:crm.claim,type_action:0
#: selection:crm.claim.report,type_action:0
msgid "Preventive Action"
msgstr ""
#. module: crm_claim
#: field:crm.claim.report,section_id:0
msgid "Section"
msgstr "Seksjon"
#. module: crm_claim
#: view:crm.claim:0
msgid "Root Causes"
msgstr ""
#. module: crm_claim
#: field:crm.claim,user_fault:0
msgid "Trouble Responsible"
msgstr "Problemer ansvarlig."
#. module: crm_claim
#: field:crm.claim,priority:0
#: view:crm.claim.report:0
#: field:crm.claim.report,priority:0
msgid "Priority"
msgstr "Prioritet"
#. module: crm_claim
#: view:crm.claim:0
msgid "Send New Email"
msgstr "Send ny e-post."
#. module: crm_claim
#: view:crm.claim:0
#: selection:crm.claim,state:0
#: view:crm.claim.report:0
msgid "New"
msgstr "Ny"
#. module: crm_claim
#: view:crm.claim:0
#: view:crm.claim.report:0
msgid "Type"
msgstr "Type"
#. module: crm_claim
#: field:crm.claim,email_from:0
msgid "Email"
msgstr "E-post"
#. module: crm_claim
#: selection:crm.claim,priority:0
#: selection:crm.claim.report,priority:0
msgid "Lowest"
msgstr "Laveste"
#. module: crm_claim
#: field:crm.claim,action_next:0
msgid "Next Action"
msgstr "Neste handling"
#. module: crm_claim
#: view:crm.claim.report:0
msgid "My Sales Team(s)"
msgstr "Mine salgs lag"
#. module: crm_claim
#: model:crm.case.stage,name:crm_claim.stage_claim3
msgid "Won't fix"
msgstr "Vil ikke fikses."
#. module: crm_claim
#: field:crm.claim,create_date:0
msgid "Creation Date"
msgstr "Opprettelses dato."
#. module: crm_claim
#: field:crm.claim,name:0
msgid "Claim Subject"
msgstr ""
#. module: crm_claim
#: model:ir.actions.act_window,help:crm_claim.action_report_crm_claim
msgid ""
"Have a general overview of all claims processed in the system by sorting "
"them with specific criteria."
msgstr ""
#. module: crm_claim
#: selection:crm.claim.report,month:0
msgid "July"
msgstr "juli"
#. module: crm_claim
#: model:ir.actions.act_window,name:crm_claim.crm_claim_stage_act
msgid "Claim Stages"
msgstr "Krav stadier."
#. module: crm_claim
#: model:ir.ui.menu,name:crm_claim.menu_crm_case_claim-act
msgid "Categories"
msgstr "Kategorier"
#. module: crm_claim
#: view:crm.claim:0
#: field:crm.claim,stage_id:0
#: view:crm.claim.report:0
#: field:crm.claim.report,stage_id:0
msgid "Stage"
msgstr "Fase"
#. module: crm_claim
#: view:crm.claim:0
msgid "History Information"
msgstr "Historisk informasjon"
#. module: crm_claim
#: view:crm.claim:0
msgid "Dates"
msgstr "Datoer"
#. module: crm_claim
#: view:crm.claim:0
msgid "Contact"
msgstr "Kontakt"
#. module: crm_claim
#: view:crm.claim.report:0
msgid "Month-1"
msgstr "Måned-1"
#. module: crm_claim
#: model:ir.actions.act_window,name:crm_claim.action_report_crm_claim
#: model:ir.ui.menu,name:crm_claim.menu_report_crm_claim_tree
msgid "Claims Analysis"
msgstr ""
#. module: crm_claim
#: help:crm.claim.report,delay_close:0
msgid "Number of Days to close the case"
msgstr "Antall dager til sak lukkes."
#. module: crm_claim
#: model:ir.model,name:crm_claim.model_crm_claim_report
msgid "CRM Claim Report"
msgstr ""
#. module: crm_claim
#: model:crm.case.stage,name:crm_claim.stage_claim1
msgid "Accepted as Claim"
msgstr ""
#. module: crm_claim
#: model:crm.case.resource.type,name:crm_claim.type_claim1
msgid "Corrective"
msgstr "Korrigerende."
#. module: crm_claim
#: selection:crm.claim.report,month:0
msgid "September"
msgstr "September."
#. module: crm_claim
#: selection:crm.claim.report,month:0
msgid "December"
msgstr "Desember"
#. module: crm_claim
#: view:crm.claim.report:0
#: field:crm.claim.report,month:0
msgid "Month"
msgstr "Måned"
#. module: crm_claim
#: field:crm.claim,type_action:0
#: view:crm.claim.report:0
#: field:crm.claim.report,type_action:0
msgid "Action Type"
msgstr "Handlings type."
#. module: crm_claim
#: field:crm.claim,write_date:0
msgid "Update Date"
msgstr "Dato oppdatert."
#. module: crm_claim
#: view:crm.claim.report:0
msgid "Year of claim"
msgstr ""
#. module: crm_claim
#: view:crm.claim.report:0
msgid "Salesman"
msgstr "Salgs man."
#. module: crm_claim
#: field:crm.claim,categ_id:0
#: view:crm.claim.report:0
#: field:crm.claim.report,categ_id:0
msgid "Category"
msgstr "Kategori"
#. module: crm_claim
#: model:crm.case.categ,name:crm_claim.categ_claim2
msgid "Value Claims"
msgstr ""
#. module: crm_claim
#: view:crm.claim:0
msgid "Responsible User"
msgstr "Ansvarlig bruker."
#. module: crm_claim
#: help:crm.claim,email_cc:0
msgid ""
"These email addresses will be added to the CC field of all inbound and "
"outbound emails for this record before being sent. Separate multiple email "
"addresses with a comma"
msgstr ""
"Disse e-postadressene vil bli lagt til kopi-feltet for alle inngående og "
"utgående e-poster før de sendes. Skill flere e-postadresser med komma."
#. module: crm_claim
#: selection:crm.claim.report,state:0
msgid "Draft"
msgstr "Kladd"
#. module: crm_claim
#: selection:crm.claim,priority:0
#: selection:crm.claim.report,priority:0
msgid "Low"
msgstr "Lav"
#. module: crm_claim
#: field:crm.claim,date_closed:0
#: selection:crm.claim,state:0
#: selection:crm.claim.report,state:0
msgid "Closed"
msgstr "Lukket"
#. module: crm_claim
#: view:crm.claim:0
msgid "Reply"
msgstr "Svar"
#. module: crm_claim
#: view:crm.claim:0
#: selection:crm.claim,state:0
#: view:crm.claim.report:0
#: selection:crm.claim.report,state:0
msgid "Pending"
msgstr "Venter"
#. module: crm_claim
#: view:crm.claim:0
msgid "Communication & History"
msgstr "Kommunikasjon & Historikk"
#. module: crm_claim
#: selection:crm.claim.report,month:0
msgid "August"
msgstr "August"
#. module: crm_claim
#: selection:crm.claim,priority:0
#: selection:crm.claim.report,priority:0
msgid "Normal"
msgstr "Normal"
#. module: crm_claim
#: view:crm.claim:0
msgid "Global CC"
msgstr ""
#. module: crm_claim
#: selection:crm.claim.report,month:0
msgid "June"
msgstr "Juni"
#. module: crm_claim
#: view:res.partner:0
msgid "Partners Claim"
msgstr ""
#. module: crm_claim
#: field:crm.claim,partner_phone:0
msgid "Phone"
msgstr "Telefon"
#. module: crm_claim
#: field:crm.claim.report,user_id:0
msgid "User"
msgstr "Bruker"
#. module: crm_claim
#: field:crm.claim,active:0
msgid "Active"
msgstr "Aktiv"
#. module: crm_claim
#: selection:crm.claim.report,month:0
msgid "November"
msgstr "November"
#. module: crm_claim
#: view:crm.claim.report:0
msgid "Extended Filters..."
msgstr "Utvidet Filtere ..."
#. module: crm_claim
#: view:crm.claim:0
msgid "Closure"
msgstr ""
#. module: crm_claim
#: view:crm.claim.report:0
msgid "Search"
msgstr "Søk"
#. module: crm_claim
#: selection:crm.claim.report,month:0
msgid "October"
msgstr "Oktober"
#. module: crm_claim
#: selection:crm.claim.report,month:0
msgid "January"
msgstr "Januar"
#. module: crm_claim
#: view:crm.claim:0
#: field:crm.claim,date:0
msgid "Claim Date"
msgstr ""
#. module: crm_claim
#: help:crm.claim,email_from:0
msgid "These people will receive email."
msgstr "Disse personene vil motta e-post."
#. module: crm_claim
#: model:ir.actions.act_window,name:crm_claim.crm_claim_categ_action
msgid "Claim Categories"
msgstr "Krav kategorier."
#. module: crm_claim
#: view:crm.claim:0
#: view:crm.claim.report:0
#: model:ir.actions.act_window,name:crm_claim.act_claim_partner
#: model:ir.actions.act_window,name:crm_claim.act_claim_partner_address
#: model:ir.actions.act_window,name:crm_claim.crm_case_categ_claim0
#: model:ir.ui.menu,name:crm_claim.menu_crm_case_claims
#: field:res.partner,claims_ids:0
msgid "Claims"
msgstr "Kravene."
#. module: crm_claim
#: selection:crm.claim,type_action:0
#: selection:crm.claim.report,type_action:0
msgid "Corrective Action"
msgstr "Korrigerende handling."
#. module: crm_claim
#: model:crm.case.categ,name:crm_claim.categ_claim3
msgid "Policy Claims"
msgstr "Politikk krav."
#. module: crm_claim
#: view:crm.claim:0
msgid "History"
msgstr "Historie"
#. module: crm_claim
#: model:ir.model,name:crm_claim.model_crm_claim
#: model:ir.ui.menu,name:crm_claim.menu_config_claim
msgid "Claim"
msgstr "Krav"
#. module: crm_claim
#: selection:crm.claim,priority:0
#: selection:crm.claim.report,priority:0
msgid "Highest"
msgstr "Høyest"
#. module: crm_claim
#: field:crm.claim,partner_address_id:0
msgid "Partner Contact"
msgstr "Partnerkontakt"
#. module: crm_claim
#: view:crm.claim:0
#: field:crm.claim,state:0
#: view:crm.claim.report:0
#: field:crm.claim.report,state:0
msgid "State"
msgstr "Stat"
#. module: crm_claim
#: view:crm.claim:0
#: view:crm.claim.report:0
msgid "Done"
msgstr "Ferdig"
#. module: crm_claim
#: view:crm.claim:0
msgid "Claim Reporter"
msgstr "Krav rapporter."
#. module: crm_claim
#: view:crm.claim:0
#: view:crm.claim.report:0
msgid "Cancel"
msgstr "Avbryt"
#. module: crm_claim
#: view:crm.claim:0
msgid "Close"
msgstr "Lukke"
#. module: crm_claim
#: view:crm.claim:0
#: view:crm.claim.report:0
#: selection:crm.claim.report,state:0
msgid "Open"
msgstr "Åpne"
#. module: crm_claim
#: view:crm.claim:0
msgid "New Claims"
msgstr "Nye krav."
#. module: crm_claim
#: view:crm.claim:0
#: selection:crm.claim,state:0
msgid "In Progress"
msgstr "I arbeid"
#. module: crm_claim
#: view:crm.claim:0
#: field:crm.claim,user_id:0
msgid "Responsible"
msgstr "Ansvarlig"
#. module: crm_claim
#: view:crm.claim.report:0
msgid "Claims created in current year"
msgstr "Krav opprettet i nåværende år."
#. module: crm_claim
#: view:crm.claim:0
msgid "Unassigned Claims"
msgstr ""
#. module: crm_claim
#: view:crm.claim.report:0
msgid "Claims created in current month"
msgstr ""
#. module: crm_claim
#: field:crm.claim.report,delay_expected:0
msgid "Overpassed Deadline"
msgstr "Passert frist"
#. module: crm_claim
#: field:crm.claim,cause:0
msgid "Root Cause"
msgstr ""
#. module: crm_claim
#: view:crm.claim:0
msgid "Claim/Action Description"
msgstr "Krav/handling beskrivelse."
#. module: crm_claim
#: field:crm.claim,description:0
msgid "Description"
msgstr "Beskrivelse."
#. module: crm_claim
#: view:crm.claim:0
msgid "Search Claims"
msgstr "Søk krav"
#. module: crm_claim
#: field:crm.claim,section_id:0
#: view:crm.claim.report:0
msgid "Sales Team"
msgstr "Salgsteam"
#. module: crm_claim
#: selection:crm.claim.report,month:0
msgid "May"
msgstr "Mai"
#. module: crm_claim
#: view:crm.claim:0
msgid "Resolution Actions"
msgstr ""
#. module: crm_claim
#: model:ir.actions.act_window,help:crm_claim.crm_case_categ_claim0
msgid ""
"Record and track your customers' claims. Claims may be linked to a sales "
"order or a lot. You can send emails with attachments and keep the full "
"history for a claim (emails sent, intervention type and so on). Claims may "
"automatically be linked to an email address using the mail gateway module."
msgstr ""
#. module: crm_claim
#: field:crm.claim.report,email:0
msgid "# Emails"
msgstr "# E-poster"
#. module: crm_claim
#: model:crm.case.stage,name:crm_claim.stage_claim2
msgid "Actions Done"
msgstr "Handlinger ferdig."
#. module: crm_claim
#: view:crm.claim.report:0
msgid "Claims created in last month"
msgstr "Krav opprettet i den siste måneden."
#. module: crm_claim
#: model:crm.case.stage,name:crm_claim.stage_claim5
msgid "Actions Defined"
msgstr ""
#. module: crm_claim
#: view:crm.claim:0
msgid "Follow Up"
msgstr "Følg opp."
#. module: crm_claim
#: help:crm.claim,state:0
msgid ""
"The state is set to 'Draft', when a case is created. "
" \n"
"If the case is in progress the state is set to 'Open'. "
" \n"
"When the case is over, the state is set to 'Done'. "
" \n"
"If the case needs to be reviewed then the state is set to 'Pending'."
msgstr ""
#. module: crm_claim
#: selection:crm.claim.report,month:0
msgid "February"
msgstr "Februar"
#. module: crm_claim
#: view:crm.claim.report:0
#: field:crm.claim.report,name:0
msgid "Year"
msgstr "År"
#. module: crm_claim
#: view:crm.claim.report:0
msgid "My company"
msgstr "Min bedrift."
#. module: crm_claim
#: selection:crm.claim.report,month:0
msgid "April"
msgstr "April"
#. module: crm_claim
#: view:crm.claim.report:0
msgid "My Case(s)"
msgstr "Min(e) sak(er)"
#. module: crm_claim
#: field:crm.claim,id:0
msgid "ID"
msgstr "ID"
#. module: crm_claim
#: constraint:res.partner:0
msgid "Error ! You cannot create recursive associated members."
msgstr "Feil! Du kan ikke opprette rekursive tilknyttede medlemmer."
#. module: crm_claim
#: view:crm.claim:0
msgid "Actions"
msgstr "Handlinger"
#. module: crm_claim
#: selection:crm.claim,priority:0
#: selection:crm.claim.report,priority:0
msgid "High"
msgstr "Høy"
#. module: crm_claim
#: model:ir.actions.act_window,help:crm_claim.crm_claim_categ_action
msgid ""
"Create claim categories to better manage and classify your claims. Some "
"example of claims can be: preventive action, corrective action."
msgstr ""
#. module: crm_claim
#: field:crm.claim.report,create_date:0
msgid "Create Date"
msgstr "Opprettet dato."
#. module: crm_claim
#: view:crm.claim:0
msgid "In Progress Claims"
msgstr ""

View File

@ -133,7 +133,7 @@ class crm_helpdesk(base_state, base_stage, osv.osv):
}
for line in msg['body'].split('\n'):
line = line.strip()
res = tools.misc.command_re.match(line)
res = tools.command_re.match(line)
if res and maps.get(res.group(1).lower()):
key = maps.get(res.group(1).lower())
update_vals[key] = res.group(2).lower()

View File

@ -63,7 +63,6 @@ class crm_lead_forward_to_partner(osv.osv_memory):
_defaults = {
'history_mode': 'latest',
'content_subtype': lambda self,cr, uid, context={}: 'html',
}
def get_record_data(self, cr, uid, model, res_id, context=None):

1008
addons/document/i18n/nb.po Normal file

File diff suppressed because it is too large Load Diff

View File

@ -18,20 +18,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import logging
import models
import edi_service
from models.edi import EDIMixin, edi_document
_logger = logging.getLogger(__name__)
# web
try:
import controllers
except ImportError:
_logger.warning(
"""Could not load openerp-web section of EDI, EDI will not behave correctly
To fix, launch openerp-web in embedded mode""")
from . import controllers
from . import models
from . import edi_service
from .models.edi import EDIMixin, edi
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

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,85 +1,32 @@
import json
import textwrap
import simplejson
import werkzeug.wrappers
import openerp.addons.web.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)
d["init"] = 's.edi.edi_import("%s");'%(url)
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)
modules = webmain.module_boot(req) + ['edi']
modules_str = ','.join(modules)
modules_json = simplejson.dumps(modules)
js = "\n ".join('<script type="text/javascript" src="%s"></script>' % i for i in webmain.manifest_list(req, modules_str, 'js'))
css = "\n ".join('<link rel="stylesheet" href="%s">' % i for i in webmain.manifest_list(req, modules_str, 'css'))
return webmain.html_template % {
'js': js,
'css': css,
'modules': modules_json,
'init': 's.edi.edi_import("%s");' % url,
}
@openerpweb.jsonrequest
def import_edi_url(self, req, url):
context = req.session.eval_context(req.context)
result = req.session.proxy('edi').import_edi_url(req.session._db, req.session._uid, req.session._password, url)
if len(result) == 1:
return {"action": webmain.clean_action(req, result[0][2])}
return {"action": webmain.clean_action(req, result[0][2], context)}
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

@ -24,15 +24,13 @@ import hashlib
import json
import logging
import re
import threading
import time
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.osv import osv, fields
from tools.translate import _
from tools.safe_eval import safe_eval as eval
_logger = logging.getLogger(__name__)
@ -74,16 +72,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 +100,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 +111,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 +149,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 +173,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 +276,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 +355,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 +400,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 +428,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:
@ -573,8 +488,10 @@ class EDIMixin(object):
if data_ids:
model = self.pool.get(model)
data = ir_model_data.browse(cr, uid, data_ids[0], context=context)
result = model.browse(cr, uid, data.res_id, context=context)
return result
if model.exists(cr, uid, [data.res_id]):
return model.browse(cr, uid, data.res_id, context=context)
# stale external-id, cleanup to allow re-import, as the corresponding record is gone
ir_model_data.unlink(cr, 1, [data_ids[0]])
def edi_import_relation(self, cr, uid, model, value, external_id, context=None):
"""Imports a M2O/M2M relation EDI specification ``[external_id,value]`` for the
@ -588,6 +505,10 @@ class EDIMixin(object):
* If previous steps gave no result, create a new record with the given
value in the target model, assign it the given external_id, and return
the new database ID
:param str value: display name of the record to import
:param str external_id: fully-qualified external ID of the record
:return: database id of newly-imported or pre-existing record
"""
_logger.debug("%s: Importing EDI relationship [%r,%r]", model, external_id, value)
target = self._edi_get_object_by_external_id(cr, uid, external_id, model, context=context)
@ -602,9 +523,11 @@ class EDIMixin(object):
self._name, external_id, value)
# also need_new_ext_id here, but already been set above
model = self.pool.get(model)
# should use name_create() but e.g. res.partner won't allow it at the moment
res_id = model.create(cr, uid, {model._rec_name: value}, context=context)
res_id, _ = model.name_create(cr, uid, value, context=context)
target = model.browse(cr, uid, res_id, context=context)
else:
_logger.debug("%s: Importing EDI relationship [%r,%r] - record already exists with ID %s, using it",
self._name, external_id, value, target.id)
if need_new_ext_id:
ext_id_members = split_external_id(external_id)
# module name is never used bare when creating ir.model.data entries, in order
@ -614,19 +537,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.get('__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 +557,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 +602,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,9 @@
##############################################################################
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 _
_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

@ -1,210 +0,0 @@
/** EDI content **/
.openerp .company_logo {
background-size: 180px 46px;
}
.oe_edi_view {
width: 65%;
vertical-align: top;
padding: 0px 25px;
border-right: 1px solid #D2CFCF;
}
.oe_edi_sidebar_container {
width: 35%;
padding: 0px 10px;
vertical-align: top;
}
button.oe_edi_action_print {
font-size: 1.5em;
margin-left: 35%;
margin-bottom: 20px;
}
button.oe_edi_action_print img {
vertical-align: bottom;
width: 32px;
height: 32px;
}
/** EDI Sidebar **/
.oe_edi_sidebar_title {
border-bottom: 1px solid #D2CFCF;
font-weight: bold;
font-size: 1.3em;
min-width: 10em;
}
.oe_edi_nested_block, .oe_edi_nested_block_import, .oe_edi_nested_block_pay {
margin: 0px 40px;
min-width: 10em;
display: none; /* made visible by click on parent input/label */
}
.oe_edi_right_top .oe_edi_nested_block label {
float: left;
text-align: right;
margin-right: 0.5em;
line-height: 180%;
font-weight: bold;
min-width: 5em;
}
.oe_edi_option {
padding-left: 5px;
line-height: 2em;
}
.oe_edi_option:hover {
background: #e8e8e8;
}
.oe_edi_import_button {
margin: 2px 10px;
white-space: nowrap;
}
.oe_edi_small, .oe_edi_small input {
font-size: 90%;
}
/** Sidebar bottom **/
.oe_edi_paypal_button {
margin: 6px;
}
/** Paperbox, from http://www.sitepoint.com/pure-css3-paper-curl/ **/
.oe_edi_paperbox {
position: relative;
width: 700px;
padding: 30px;
padding-bottom: 50px;
margin: 20px auto;
background-color: #fff;
-webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.2), inset 0 0 50px rgba(0, 0, 0, 0.1);
-moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.2), inset 0 0 50px rgba(0, 0, 0, 0.1);
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2), inset 0 0 50px rgba(0, 0, 0, 0.1);
}
.oe_edi_paperbox:before, .oe_edi_paperbox:after {
position: absolute;
width: 40%;
height: 10px;
content: ' ';
left: 12px;
bottom: 15px;
background: transparent;
-webkit-transform: skew(-5deg) rotate(-5deg);
-moz-transform: skew(-5deg) rotate(-5deg);
-ms-transform: skew(-5deg) rotate(-5deg);
-o-transform: skew(-5deg) rotate(-5deg);
transform: skew(-5deg) rotate(-5deg);
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
-moz-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
z-index: -1;
}
.oe_edi_paperbox:after {
left: auto; right: 12px;
-webkit-transform: skew(5deg) rotate(5deg);
-moz-transform: skew(5deg) rotate(5deg);
-ms-transform: skew(5deg) rotate(5deg);
-o-transform: skew(5deg) rotate(5deg);
transform: skew(5deg) rotate(5deg);
}
/** Sale Order / Purchase Order Preview **/
table.oe_edi_data, .oe_edi_doc_title {
border-collapse: collapse;
clear: both;
}
.oe_edi_data th {
white-space: nowrap;
}
.oe_edi_data .oe_edi_floor {
border-bottom: 1px solid black;
}
.oe_edi_data .oe_edi_ceiling {
border-top: 1px solid black;
}
.oe_edi_data .oe_edi_data_row {
border-bottom: 1px solid #D2CFCF;
}
.oe_edi_data_row td {
vertical-align: top;
}
.oe_edi_inner_note {
font-style: italic;
font-size: 95%;
padding-left: 10px;
/* prevent wide notes from disrupting layout due to <pre> styling */
white-space: pre-line;
width: 90%;
}
.oe_edi_data_row .oe_edi_inner_note {
/* prevent wide notes from disrupting layout due to <pre> styling */
width: 25em;
}
.oe_edi_shade {
background: #e8e8e8;
}
.oe_edi_company_name {
text-transform: uppercase;
font-weight: bold;
}
.oe_edi_address_from {
float: left;
}
.oe_edi_address_to {
float: right;
margin-top: 25px;
margin-bottom: 30px;
}
.oe_edi_company_block_title {
width: 375px;
margin: 0px;
padding: 2px 14px;
background-color: #252525;
border-top-left-radius: 5px 5px;
border-top-right-radius: 5px 5px;
background-repeat: repeat no-repeat;
}
.oe_edi_company_block_title .oe_edi_company_name {
margin: 0px;
font-size: 1em;
color: #FFF;
}
.oe_edi_company_block_body {
width: 375px;
margin: 0px;
padding: 5px 14px;
line-height: 16px;
background-color: rgb(242, 242, 242);
}
.oe_edi_company_block_body p {
color: #222;
margin: 5px 0px;
}
.oe_edi_summary_label {
float: left;
}
.oe_edi_summary_value {
float: right;
}
/** Python code highlighting **/
/* GeSHi (C) 2004 - 2007 Nigel McNie, 2007 - 2008 Benny Baumann
(http://qbnz.com/highlighter/ and http://geshi.org/) */
.python .de1, .python .de2 {font: normal normal 1em/1.2em monospace; margin:0; padding:0; background:none; vertical-align:top;}
.python {font-family:monospace;}
.python .imp {font-weight: bold; color: red;}
.python li, .python .li1 {background: #ffffff; list-style: none;}
.python .ln {width:1px;text-align:right;margin:0;padding:0 2px;vertical-align:top;}
.python .li2 {background: #f8f8f8;}
.python .kw1 {color: #ff7700;font-weight:bold;}
.python .kw2 {color: #008000;}
.python .kw3 {color: #dc143c;}
.python .kw4 {color: #0000cd;}
.python .co1 {color: #808080; font-style: italic;}
.python .coMULTI {color: #808080; font-style: italic;}
.python .es0 {color: #000099; font-weight: bold;}
.python .br0 {color: black;}
.python .sy0 {color: #66cc66;}
.python .st0 {color: #483d8b;}
.python .nu0 {color: #ff4500;}
.python .me1 {color: black;}
.python span.xtra { display:block; }
.python ol { padding: 0px; }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

View File

@ -1,119 +1,9 @@
openerp.edi = function(openerp) {
openerp.edi = {}
openerp.edi = function(instance) {
var _t = instance.web._t;
instance.edi = {}
openerp.edi.EdiView = openerp.web.Widget.extend({
init: function(parent, db, token) {
this._super();
this.db = db;
this.token = token;
this.session = openerp.session;
this.template = "EdiEmpty";
this.content = "";
this.sidebar = "";
},
start: function() {
this._super();
var self = this;
var param = {"db": self.db, "token": self.token};
return self.rpc('/edi/get_edi_document', param).done(this.on_document_loaded).fail(this.on_document_failed);
},
on_document_loaded: function(docs){
this.doc = docs[0];
var template_content = "Edi." + this.doc.__model + ".content";
var template_sidebar = "Edi." + this.doc.__model + ".sidebar";
var param = {"widget":this, "doc":this.doc};
if (openerp.web.qweb.templates[template_sidebar]) {
this.sidebar = openerp.web.qweb.render(template_sidebar, param);
}
if (openerp.web.qweb.templates[template_content]) {
this.content = openerp.web.qweb.render(template_content, param);
}
this.$el.html(openerp.web.qweb.render("EdiView", param));
this.$el.find('button.oe_edi_action_print').bind('click', this.do_print);
this.$el.find('button#oe_edi_import_existing').bind('click', this.do_import_existing);
this.$el.find('button#oe_edi_import_create').bind('click', this.do_import_create);
this.$el.find('button#oe_edi_download').bind('click', this.do_download);
this.$el.find('.oe_edi_import_choice, .oe_edi_import_choice_label').bind('click', this.toggle_choice('import'));
this.$el.find('.oe_edi_pay_choice, .oe_edi_pay_choice_label').bind('click', this.toggle_choice('pay'));
this.$el.find('#oe_edi_download_show_code').bind('click', this.show_code);
},
on_document_failed: function(response) {
var self = this;
var params = {
error: response,
//TODO: should this be _t() wrapped?
message: "Sorry, this document cannot be located. Perhaps the link you are using has expired?"
}
$(openerp.web.qweb.render("DialogWarning", params)).dialog({
title: "Document not found",
modal: true,
});
},
show_code: function($event) {
$('#oe_edi_download_code').toggle();
},
get_download_url: function() {
var l = window.location;
var url_prefix = l.protocol + '//' + l.host;
return url_prefix +'/edi/download?db=' + this.db + '&token=' + this.token;
},
get_paypal_url: function(document_type, ref_field) {
var comp_name = encodeURIComponent(this.doc.company_id[1]);
var doc_ref = encodeURIComponent(this.doc[ref_field]);
var paypal_account = encodeURIComponent(this.doc.company_address.paypal_account);
var amount = encodeURIComponent(this.doc.amount_total);
var cur_code = encodeURIComponent(this.doc.currency.code);
var paypal_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick" +
"&business=" + paypal_account +
"&item_name=" + document_type + "%20" + comp_name + "%20" + doc_ref +
"&invoice=" + doc_ref +
"&amount=" + amount +
"&currency_code=" + cur_code +
"&button_subtype=services&amp;no_note=1&amp;bn=OpenERP_PayNow_" + cur_code;
return paypal_url;
},
toggle_choice: function(mode) {
return function($e) {
$('.oe_edi_nested_block_'+mode).hide();
$('.'+$e.target.id+'_nested').show();
return true;
}
},
do_print: function(e){
var l = window.location;
window.location = l.protocol + '//' + l.host + "/edi/download_attachment?db=" + this.db + "&token=" + this.token;
},
do_import_existing: function(e) {
var url_download = this.get_download_url();
var $edi_text_server_input = this.$el.find('#oe_edi_txt_server_url');
var server_url = $edi_text_server_input.val();
$edi_text_server_input.removeClass('invalid');
if (!server_url) {
$edi_text_server_input.addClass('invalid');
return false;
}
var protocol = "http://";
if (server_url.toLowerCase().lastIndexOf('http', 0) == 0 ) {
protocol = '';
}
window.location = protocol + server_url + '/edi/import_url?url=' + encodeURIComponent(url_download);
},
do_import_create: function(e){
var url_download = this.get_download_url();
window.location = "https://cc.my.openerp.com/odms/create_edi?url=" + encodeURIComponent(url_download);
},
do_download: function(e){
window.location = this.get_download_url();
}
});
instance.edi.EdiImport = instance.web.Widget.extend({
openerp.edi.edi_view = function (db, token) {
openerp.session.session_bind().done(function () {
new openerp.edi.EdiView(null,db,token).appendTo($("body").addClass('openerp'));
});
}
openerp.edi.EdiImport = openerp.web.Widget.extend({
init: function(parent,url) {
this._super();
this.url = url;
@ -137,7 +27,7 @@ openerp.edi.EdiImport = openerp.web.Widget.extend({
show_login: function() {
this.destroy_content();
this.login = new openerp.web.Login(this);
this.login = new instance.web.Login(this);
this.login.appendTo(this.$el);
},
@ -167,18 +57,18 @@ openerp.edi.EdiImport = openerp.web.Widget.extend({
window.location = "/";
}
}
}).html('The document has been successfully imported!');
}).html(_t('The document has been successfully imported!'));
}
},
on_imported_error: function(response){
var self = this;
var msg = "Sorry, the document could not be imported.";
var msg = _t("Sorry, the document could not be imported.");
if (response.data.fault_code) {
msg += "\n Reason:" + response.data.fault_code;
msg += "\n " + _t("Reason:") + response.data.fault_code;
}
var params = {error: response, message: msg};
$(openerp.web.qweb.render("CrashManagerWarning", params)).dialog({
title: "Document Import Notification",
$(instance.web.qweb.render("CrashManager.warning", params)).dialog({
title: _t("Document Import Notification"),
modal: true,
buttons: {
Ok: function() { $(this).dialog("close"); }
@ -187,9 +77,9 @@ openerp.edi.EdiImport = openerp.web.Widget.extend({
}
});
openerp.edi.edi_import = function (url) {
openerp.session.session_bind().done(function () {
new openerp.edi.EdiImport(null,url).appendTo($("body").addClass('openerp'));
instance.edi.edi_import = function (url) {
instance.session.session_bind().done(function () {
new instance.edi.EdiImport(null,url).appendTo($("body").addClass('openerp'));
});
}

View File

@ -1,93 +0,0 @@
<template>
<t t-name="EdiEmpty">
<div style="height:100%;"></div>
</t>
<t t-name="EdiImport">
<t t-call="WebClient"/>
</t>
<t t-name="EdiView">
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%" id="oe_app" class="oe-application oe_forms oe_semantic_html_override">
<tr>
<td colspan="2" valign="top" id="oe_header" class="header">
<div> <a href="/" class="company_logo_link">
<div class="company_logo"
t-att-style="'background-size: 180px 46px; background: url('+ (doc.company_address ? '/edi/binary?db='+widget.db+'&amp;token='+widget.token : '/web/static/src/img/logo.png')+')'"/></a> </div>
</td>
</tr>
<tr>
<td colspan="2" valign="top" height="100%">
<table cellspacing="0" cellpadding="0" border="0" height="100%" width="100%">
<tr>
<td class="oe_edi_view">
<p class="oe_paragraph"><t t-raw="widget.content"/></p>
<button type="button" class="oe_edi_action_print">
View/Print <img src="/edi/static/src/img/pdf.png"/>
</button>
</td>
<td class="oe_edi_sidebar_container">
<p class="oe_edi_sidebar_title">
Import this document
</p>
<div class="oe_edi_option">
<input type="radio" id="oe_edi_import_openerp" name="oe_edi_import" class="oe_edi_import_choice"/>
<label for="oe_edi_import_openerp" id="oe_edi_import_openerp" class="oe_edi_import_choice_label">Import it into an existing OpenERP instance</label>
</div>
<p class="oe_edi_nested_block_import oe_edi_import_openerp_nested">
<label for="oe_edi_txt_server_url">OpenERP instance address:</label>
<br/>
<input type="text" id="oe_edi_txt_server_url" placeholder="http://example.my.openerp.com/"/><br/>
<button type="button" class="oe_edi_import_button" id="oe_edi_import_existing">Import</button>
</p>
<div class="oe_edi_option">
<input type="radio" id="oe_edi_import_saas" name="oe_edi_import" class="oe_edi_import_choice"/>
<label for="oe_edi_import_saas" id="oe_edi_import_saas" class="oe_edi_import_choice_label">Import it into a new OpenERP Online instance</label>
</div>
<p class="oe_edi_nested_block_import oe_edi_import_saas_nested">
<button type="button" class="oe_edi_import_button" id="oe_edi_import_create">Create my new OpenERP instance</button>
</p>
<div class="oe_edi_option">
<input type="radio" id="oe_edi_import_download" name="oe_edi_import" class="oe_edi_import_choice"/>
<label for="oe_edi_import_download" id="oe_edi_import_download" class="oe_edi_import_choice_label">Import into another application</label>
</div>
<p class="oe_edi_nested_block_import oe_edi_small oe_edi_import_download_nested">
OpenERP's Electronic Data Interchange documents are based on a generic and language
independent <a href="http://json.org">JSON</a> serialization of the document's attribute.
It is usually very quick and straightforward to create a small plug-in for your preferred
application that will be capable of importing any OpenERP EDI document.
You can find out more details about how to do this and what the content of OpenERP EDI documents
is like in the <a href="http://doc.openerp.com/search.html?q=edi">OpenERP documentation</a>.
<br/>
To get started immediately, <a href="#" id="oe_edi_download_show_code">see is all it takes to use this EDI document in Python</a>.
</p>
<div class="python oe_edi_nested_block_import oe_edi_small" id="oe_edi_download_code" t-translation="off">
<ol><li class="li1"><div class="de1"><span class="kw1">import</span> <span class="kw3">urllib2</span><span class="sy0">,</span> simplejson</div></li>
<li class="li1"><div class="de1">edi_document <span class="sy0">=</span> <span class="kw3">urllib2</span>.<span class="me1">urlopen</span><span class="br0">(</span><span class="st0">'<t t-esc="widget.get_download_url()"/>'</span><span class="br0">)</span>.<span class="me1">read</span><span class="br0">(</span><span class="br0">)</span></div></li>
<li class="li2"><div class="de2">document_data <span class="sy0">=</span> simplejson.<span class="me1">loads</span><span class="br0">(</span>edi_document<span class="br0">)</span><span class="br0">[</span><span class="nu0">0</span><span class="br0">]</span></div></li>
<li class="li1"><div class="de1"><span class="kw1">print</span> <span class="st0">"Amount: "</span><span class="sy0">,</span> document_data<span class="br0">[</span><span class="st0">'amount_total'</span><span class="br0">]</span></div></li>
</ol></div>
<p class="oe_edi_nested_block_import oe_edi_small oe_edi_import_download_nested">
You can download the raw EDI document here:<br/>
<input type="text" readonly="readonly" t-att-value="widget.get_download_url()"/>
<button type="button" class="oe_edi_import_button" id="oe_edi_download">Download</button>
</p>
<div class="oe_edi_right_bottom">
<t t-raw="widget.sidebar"/>
</div>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td colspan="2">
<div id="oe_footer" class="oe_footer">
<p class="oe_footer_powered">Powered by <a href="http://www.openerp.com">OpenERP</a></p>
</div>
</td>
</tr>
</table>
</t>
</template>

View File

@ -1,163 +0,0 @@
<template>
<t t-name="Edi.account.invoice.content">
<div class="oe_edi_paperbox">
<div class="oe_edi_address_from">
<div class="oe_edi_company_block_title">
<span class="oe_edi_company_name"><t t-esc="doc.company_id[1]"/></span>
</div>
<div class="oe_edi_company_block_body">
<p>
<t t-if="doc.company_address">
<t t-if="doc.company_address.street" t-esc="doc.company_address.street"/><br/>
<t t-if="doc.company_address.street2"><t t-esc="doc.company_address.street2"/><br/></t>
<t t-if="doc.company_address.zip" t-esc="doc.company_address.zip"/> <t t-if="doc.company_address.city" t-esc="doc.company_address.city"/> <br/>
<t t-if="doc.company_address.country_id"><t t-esc="doc.company_address.country_id[1]"/><br/></t>
</t>
</p>
</div>
</div>
<div class="oe_edi_address_to">
<div class="oe_edi_company_block_title">
<span class="oe_edi_company_name"><t t-esc="doc.partner_id[1]"/></span>
</div>
<div class="oe_edi_company_block_body">
<p>
<t t-if="doc.partner_address">
<t t-if="doc.partner_address.street" t-esc="doc.partner_address.street"/><br/>
<t t-if="doc.partner_address.street2"><t t-esc="doc.partner_address.street2"/><br/></t>
<t t-if="doc.partner_address.zip" t-esc="doc.partner_address.zip"/> <t t-if="doc.partner_address.city" t-esc="doc.partner_address.city"/> <br/>
<t t-if="doc.partner_address.country_id"><t t-esc="doc.partner_address.country_id[1]"/><br/></t>
</t>
</p>
</div>
</div>
<h1 class="oe_edi_doc_title">Invoice <t t-esc="doc.internal_number"/>: <t t-esc="_.str.sprintf('%.2f',doc.amount_total)"/> <t t-esc="doc.currency.code"/></h1>
<table width="100%" class="oe_edi_data oe_edi_shade">
<tr class="oe_edi_floor">
<th align="left">Description</th>
<th align="left">Date</th>
<th align="left">Your Reference</th>
</tr>
<tr class="oe_edi_data_row">
<td align="left"><t t-if="doc.name" t-esc="doc.name"/></td>
<td align="left"><t t-if="doc.date_invoice" t-esc="doc.date_invoice"/></td>
<td align="left"><t t-if="doc.partner_ref" t-esc="doc.partner_ref"/></td>
</tr>
</table>
<p/>
<table width="100%" class="oe_edi_data">
<tr class="oe_edi_floor">
<th align="left">Product Description</th>
<th align="right">Quantity</th>
<th align="right">Unit Price</th>
<th align="right">Discount</th>
<th align="right">Price</th>
</tr>
<t t-if="doc.invoice_line" t-foreach="doc.invoice_line" t-as="invoice_line">
<tr class="oe_edi_data_row">
<td align="left"><t t-esc="invoice_line.name"/>
<t t-if="invoice_line.note">
<pre class="oe_edi_inner_note"><t t-esc="invoice_line.note"/></pre>
</t>
</td>
<td align="right"><t t-esc="_.str.sprintf('%.2f',invoice_line.quantity)"/> <t t-esc="invoice_line.uos_id[1]"/></td>
<td align="right"><t t-esc="_.str.sprintf('%.2f',invoice_line.price_unit)"/></td>
<td align="right"><t t-esc="_.str.sprintf('%.2f',invoice_line.discount)"/></td>
<td align="right"><t t-esc="_.str.sprintf('%.2f',invoice_line.price_subtotal)"/> <t t-esc="doc.currency.code"/></td>
</tr>
</t>
<tr>
<td colspan="3"></td>
<td colspan="2" class="oe_edi_ceiling">
<div class="oe_edi_summary_label">
Net Total:
</div>
<div class="oe_edi_summary_value">
<t t-esc="_.str.sprintf('%.2f',doc.amount_untaxed)"/> <t t-esc="doc.currency.code"/>
</div>
</td>
</tr>
<tr>
<td colspan="3"></td>
<td colspan="2" class="oe_edi_floor">
<div class="oe_edi_summary_label">
Taxes:
</div>
<div class="oe_edi_summary_value">
<t t-esc="_.str.sprintf('%.2f',doc.amount_tax)"/> <t t-esc="doc.currency.code"/>
</div>
</td>
</tr>
<tr>
<td colspan="3"></td>
<th colspan="2" class="oe_edi_shade">
<div class="oe_edi_summary_label">
Total:
</div>
<div class="oe_edi_summary_value">
<t t-esc="_.str.sprintf('%.2f',doc.amount_total)"/> <t t-esc="doc.currency.code"/>
</div>
</th>
</tr>
</table>
<t t-if="doc.tax_line">
<table class="oe_edi_data" width="40%">
<tr class="oe_edi_floor">
<th align="left">Tax</th>
<th align="right">Base Amount</th>
<th align="right">Amount</th>
</tr>
<t t-if="doc.tax_line"><t t-foreach="doc.tax_line" t-as="tax_line">
<tr class="oe_edi_data_row">
<td align="left"><t t-esc="tax_line.name"/></td>
<td align="right"><t t-esc="_.str.sprintf('%.2f',tax_line.base_amount)"/> <t t-esc="doc.currency.code"/></td>
<td align="right"><t t-esc="_.str.sprintf('%.2f',tax_line.amount)"/> <t t-esc="doc.currency.code"/></td>
</tr>
</t>
</t>
</table>
</t>
<t t-if="doc.comment">
<p>Notes:</p>
<pre class="oe_edi_inner_note"><t t-esc="doc.comment"/></pre>
</t>
</div>
</t>
<t t-name="Edi.account.invoice.sidebar">
<t t-if="!doc.reconciled &amp;&amp; (doc.type == 'out_invoice' or doc.type == 'in_refund')">
<t t-if="doc.company_address.paypal_account || doc.company_address.bank_ids">
<p class="oe_edi_sidebar_title">Pay Online</p>
<t t-if="doc.company_address.paypal_account">
<div class="oe_edi_option">
<input type="radio" id="oe_edi_paypal" name="oe_edi_pay" class="oe_edi_pay_choice"/>
<label for="oe_edi_paypal" id="oe_edi_paypal" class="oe_edi_pay_choice_label">Paypal</label>
</div>
<p class="oe_edi_nested_block_pay oe_edi_paypal_nested">
You may directly pay this invoice online via Paypal's secure payment gateway:<br/>
<a t-att-href="widget.get_paypal_url('Invoice','internal_number')" target="_new">
<img class="oe_edi_paypal_button" src="https://www.paypal.com/en_US/i/btn/btn_paynowCC_LG.gif"/>
</a>
</p>
</t>
<t t-if="doc.company_address.bank_ids">
<div class="oe_edi_option">
<input type="radio" id="oe_edi_pay_wire" name="oe_edi_pay" class="oe_edi_pay_choice"/>
<label for="oe_edi_pay_wire" id="oe_edi_pay_wire" class="oe_edi_pay_choice_label">Bank Wire Transfer</label>
</div>
<p class="oe_edi_nested_block_pay oe_edi_pay_wire_nested">
Please transfer <strong><t t-esc="_.str.sprintf('%.2f',doc.amount_total)"/> <t t-esc="doc.currency.code"/></strong> to
<strong><t t-esc="doc.company_id[1]"/></strong> (postal address on the invoice header)
using one of the following bank accounts. Be sure to mention the invoice
reference <strong><t t-esc="doc.internal_number"/></strong> on the transfer:
<br/><br/>
</p>
<ul class="oe_edi_nested_block_pay oe_edi_pay_wire_nested">
<t t-foreach="doc.company_address.bank_ids" t-as="bank_info">
<li><t t-esc="bank_info[1]"/></li>
</t>
</ul>
</t>
</t>
</t>
</t>
</template>

View File

@ -1,169 +0,0 @@
<template>
<t t-name="Edi.sale.order.content">
<div class="oe_edi_paperbox">
<div class="oe_edi_address_from">
<div class="oe_edi_company_block_title">
<span class="oe_edi_company_name"><t t-esc="doc.company_id[1]"/></span>
</div>
<div class="oe_edi_company_block_body">
<p>
<t t-if="doc.company_address">
<t t-if="doc.company_address.street" t-esc="doc.company_address.street"/><br/>
<t t-if="doc.company_address.street2"><t t-esc="doc.company_address.street2"/><br/></t>
<t t-if="doc.company_address.zip" t-esc="doc.company_address.zip"/> <t t-if="doc.company_address.city" t-esc="doc.company_address.city"/> <br/>
<t t-if="doc.company_address.country_id"><t t-esc="doc.company_address.country_id[1]"/><br/></t>
</t>
</p>
</div>
</div>
<div class="oe_edi_address_to">
<div class="oe_edi_company_block_title">
<span class="oe_edi_company_name"><t t-esc="doc.partner_id[1]"/></span>
</div>
<div class="oe_edi_company_block_body">
<p>
<t t-if="doc.partner_address">
<t t-if="doc.partner_address.street" t-esc="doc.partner_address.street"/><br/>
<t t-if="doc.partner_address.street2"><t t-esc="doc.partner_address.street2"/><br/></t>
<t t-if="doc.partner_address.zip" t-esc="doc.partner_address.zip"/> <t t-if="doc.partner_address.city" t-esc="doc.partner_address.city"/> <br/>
<t t-if="doc.partner_address.country_id"><t t-esc="doc.partner_address.country_id[1]"/><br/></t>
</t>
</p>
</div>
</div>
<t t-if="(doc.state == 'draft' or doc.state == 'sent') and doc.__model == 'sale.order'">
<h1 class="oe_edi_doc_title">Quotation <t t-esc="doc.name"/>: <t t-esc="_.str.sprintf('%.2f',doc.amount_total)"/> <t t-esc="doc.currency.code"/></h1>
</t>
<t t-if="(doc.state == 'draft' or doc.state == 'sent') and doc.__model == 'purchase.order'">
<h1 class="oe_edi_doc_title">Request for Quotation <t t-esc="doc.name"/>: <t t-esc="_.str.sprintf('%.2f',doc.amount_total)"/> <t t-esc="doc.currency.code"/></h1>
</t>
<t t-if="(doc.state != 'draft' and doc.state != 'sent')">
<h1 class="oe_edi_doc_title">Order <t t-esc="doc.name"/>: <t t-esc="_.str.sprintf('%.2f',doc.amount_total)"/> <t t-esc="doc.currency.code"/></h1>
</t>
<table width="100%" class="oe_edi_data oe_edi_shade">
<tr class="oe_edi_floor">
<th align="left">Your Reference</th>
<th align="left">Date</th>
<th align="left">Salesperson</th>
<th align="left">Payment terms</th>
</tr>
<tr class="oe_edi_data_row">
<td align="left"><t t-if="doc.partner_ref" t-esc="doc.partner_ref"/></td>
<td align="left"><t t-esc="doc.date_order"/></td>
<td align="left"><t t-if="doc.user_id" t-esc="doc.user_id[1]"/></td>
<td align="left">
<t t-if="doc.payment_term" t-esc="doc.payment_term[1]"/>
</td>
</tr>
</table>
<p/>
<table width="100%" class="oe_edi_data">
<tr class="oe_edi_floor">
<th align="left">Product Description</th>
<th align="right">Quantity</th>
<th align="right">Unit Price</th>
<th align="right">Discount(%)</th>
<th align="right">Price</th>
</tr>
<t t-if="doc.order_line" t-foreach="doc.order_line" t-as="doc_line">
<tr class="oe_edi_data_row">
<td align="left"><t t-esc="doc_line.name"/>
<t t-if="doc_line.notes">
<pre class="oe_edi_inner_note"><t t-esc="doc_line.notes"/></pre>
</t>
</td>
<td align="right">
<t t-esc="_.str.sprintf('%.2f',doc_line.product_qty)"/> <t t-esc="doc_line.product_uom[1]"/>
</td>
<td align="right"><t t-esc="_.str.sprintf('%.2f',doc_line.price_unit)"/></td>
<td align="right"><t t-esc="_.str.sprintf('%.2f',doc_line.discount or 0.0)"/></td>
<td align="right"><t t-esc="_.str.sprintf('%.2f',doc_line.price_subtotal)"/> <t t-esc="doc.currency.code"/></td>
</tr>
</t>
<tr>
<td colspan="3"></td>
<td colspan="2" class="oe_edi_ceiling">
<div class="oe_edi_summary_label">
Net Total:
</div>
<div class="oe_edi_summary_value">
<t t-esc="_.str.sprintf('%.2f',doc.amount_untaxed)"/> <t t-esc="doc.currency.code"/>
</div>
</td>
</tr>
<tr>
<td colspan="3"></td>
<td colspan="2" class="oe_edi_floor">
<div class="oe_edi_summary_label">
Taxes:
</div>
<div class="oe_edi_summary_value">
<t t-esc="_.str.sprintf('%.2f',doc.amount_tax)"/> <t t-esc="doc.currency.code"/>
</div>
</td>
</tr>
<tr>
<td colspan="3"></td>
<th colspan="2" class="oe_edi_shade">
<div class="oe_edi_summary_label">
Total:
</div>
<div class="oe_edi_summary_value">
<t t-esc="_.str.sprintf('%.2f',doc.amount_total)"/> <t t-esc="doc.currency.code"/>
</div>
</th>
</tr>
</table>
<t t-if="doc.notes">
<p>Notes:</p>
<pre class="oe_edi_inner_note"><t t-esc="doc.notes"/></pre>
</t>
</div>
</t>
<t t-name="Edi.sale.order.sidebar">
<t t-if="doc.order_policy and (doc.order_policy == 'prepaid' || doc.order_policy == 'manual') and (doc.state != 'draft' and doc.state != 'sent')">
<t t-if="doc.company_address.paypal_account || doc.company_address.bank_ids">
<p class="oe_edi_sidebar_title">Pay Online</p>
<t t-if="doc.company_address.paypal_account">
<div class="oe_edi_option">
<input type="radio" id="oe_edi_paypal" name="oe_edi_pay" class="oe_edi_pay_choice"/>
<label for="oe_edi_paypal" id="oe_edi_paypal" class="oe_edi_pay_choice_label">Paypal</label>
</div>
<p class="oe_edi_nested_block_pay oe_edi_paypal_nested">
You may directly pay this order online via Paypal's secure payment gateway:<br/>
<a t-att-href="widget.get_paypal_url('Sale Order','name')" target="_new">
<img class="oe_edi_paypal_button" src="https://www.paypal.com/en_US/i/btn/btn_paynowCC_LG.gif"/>
</a>
</p>
</t>
<t t-if="doc.company_address.bank_ids">
<div class="oe_edi_option">
<input type="radio" id="oe_edi_pay_wire" name="oe_edi_pay" class="oe_edi_pay_choice"/>
<label for="oe_edi_pay_wire" id="oe_edi_pay_wire" class="oe_edi_pay_choice_label">Bank Wire Transfer</label>
</div>
<p class="oe_edi_nested_block_pay oe_edi_pay_wire_nested">
Please transfer <strong><t t-esc="_.str.sprintf('%.2f',doc.amount_total)"/> <t t-esc="doc.currency.code"/></strong> to
<strong><t t-esc="doc.company_id[1]"/></strong> (postal address on the order header)
using one of the following bank accounts. Be sure to mention the document
reference <strong><t t-esc="doc.name"/></strong> on the transfer:
<br/><br/>
</p>
<ul class="oe_edi_nested_block_pay oe_edi_pay_wire_nested">
<t t-foreach="doc.company_address.bank_ids" t-as="bank_info">
<li><t t-esc="bank_info[1]"/></li>
</t>
</ul>
</t>
</t>
</t>
</t>
<t t-name="Edi.purchase.order.content">
<t t-call="Edi.sale.order.content"/>
</t>
<t t-name="Edi.purchase.order.sidebar">
<t t-call="Edi.sale.order.sidebar"/>
</t>
</template>

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

@ -100,25 +100,27 @@ class test_message_compose(test_mail_mockup.TestMailMockups):
# ----------------------------------------
# 1. Comment on pigs
compose_id = mail_compose.create(cr, uid,
{'subject': 'Forget me subject', 'body': 'Dummy body'},
{'default_composition_mode': 'comment',
'default_model': 'mail.group',
'default_res_id': self.group_pigs_id,
'default_use_template': False,
'default_template_id': email_template_id,
'active_ids': [self.group_pigs_id, self.group_bird_id]})
compose = mail_compose.browse(cr, uid, compose_id)
# 2. Perform 'toggle_template', to set use_template and use template_id
mail_compose.toggle_template(cr, uid, [compose_id], {'default_composition_mode': 'comment', 'default_model': 'mail.group'})
context = {
'default_composition_mode': 'comment',
'default_model': 'mail.group',
'default_res_id': self.group_pigs_id,
'default_use_template': False,
'default_template_id': email_template_id,
'active_ids': [self.group_pigs_id, self.group_bird_id]
}
compose_id = mail_compose.create(cr, uid, {'subject': 'Forget me subject', 'body': 'Dummy body'}, context)
compose = mail_compose.browse(cr, uid, compose_id, context)
onchange_res = compose.onchange_template_id(email_template_id, 'comment', 'mail.group', self.group_pigs_id)['value']
onchange_res['partner_ids'] = [(4, partner_id) for partner_id in onchange_res.pop('partner_ids', [])]
onchange_res['attachment_ids'] = [(4, attachment_id) for attachment_id in onchange_res.pop('attachment_ids', [])]
compose.write(onchange_res)
compose.refresh()
message_pids = [partner.id for partner in compose.partner_ids]
partner_ids = self.res_partner.search(cr, uid, [('email', 'in', ['b@b.b', 'c@c.c', 'd@d.d'])])
# Test: mail.compose.message: subject, body, content_subtype, partner_ids
# Test: mail.compose.message: subject, body, partner_ids
self.assertEqual(compose.subject, _subject1, 'mail.compose.message subject incorrect')
self.assertEqual(compose.body, _body_html1, 'mail.compose.message body incorrect')
self.assertEqual(compose.content_subtype, 'html', 'mail.compose.message content_subtype incorrect')
self.assertEqual(set(message_pids), set(partner_ids), 'mail.compose.message partner_ids incorrect')
# Test: mail.compose.message: attachments
# Test: mail.message: attachments
@ -128,41 +130,34 @@ class test_message_compose(test_mail_mockup.TestMailMockups):
self.assertIn((attach.name, base64.b64decode(attach.datas)), _attachments_test,
'mail.message attachment name / data incorrect')
# 3. Perform 'toggle_template': template is not set anymore
mail_compose.toggle_template(cr, uid, [compose_id], {'default_composition_mode': 'comment', 'default_model': 'mail.group'})
compose.refresh()
# Test: subject, body, partner_ids
self.assertEqual(compose.subject, False, 'mail.compose.message subject incorrect')
self.assertEqual(compose.body, '', 'mail.compose.message body incorrect')
# ----------------------------------------
# CASE3: mass_mail with template
# ----------------------------------------
# 1. Mass_mail on pigs and bird, with a default_partner_ids set to check he is correctly added
compose_id = mail_compose.create(cr, uid,
{'subject': 'Forget me subject', 'body': 'Dummy body'},
{'default_composition_mode': 'mass_mail',
'default_model': 'mail.group',
'default_res_id': self.group_pigs_id,
'default_use_template': False,
'default_template_id': email_template_id,
'default_partner_ids': [p_a_id],
'active_ids': [self.group_pigs_id, self.group_bird_id]})
compose = mail_compose.browse(cr, uid, compose_id)
# 2. Perform 'toggle_template', to set use_template and use template_id
mail_compose.toggle_template(cr, uid, [compose_id], {'default_composition_mode': 'comment', 'default_model': 'mail.group'})
context = {
'default_composition_mode': 'mass_mail',
'default_model': 'mail.group',
'default_res_id': self.group_pigs_id,
'default_template_id': email_template_id,
'default_partner_ids': [p_a_id],
'active_ids': [self.group_pigs_id, self.group_bird_id]
}
compose_id = mail_compose.create(cr, uid, {'subject': 'Forget me subject', 'body': 'Dummy body'}, context)
compose = mail_compose.browse(cr, uid, compose_id, context)
onchange_res = compose.onchange_template_id(email_template_id, 'mass_mail', 'mail.group', self.group_pigs_id)['value']
onchange_res['partner_ids'] = [(4, partner_id) for partner_id in onchange_res.pop('partner_ids', [])]
onchange_res['attachment_ids'] = [(4, attachment_id) for attachment_id in onchange_res.pop('attachment_ids', [])]
compose.write(onchange_res)
compose.refresh()
message_pids = [partner.id for partner in compose.partner_ids]
partner_ids = [p_a_id]
# Test: mail.compose.message: subject, body, content_subtype, partner_ids
self.assertEqual(compose.subject, '${object.name}', 'mail.compose.message subject incorrect')
self.assertEqual(compose.body, '${object.description}', 'mail.compose.message body incorrect')
self.assertEqual(compose.content_subtype, 'html', 'mail.compose.message content_subtype incorrect')
self.assertEqual(set(message_pids), set(partner_ids), 'mail.compose.message partner_ids incorrect')
# 3. Post the comment, get created message
# 2. Post the comment, get created message
mail_compose.send_mail(cr, uid, [compose_id], {'default_res_id': -1, 'active_ids': [self.group_pigs_id, self.group_bird_id]})
group_pigs.refresh()
group_bird.refresh()

View File

@ -19,11 +19,8 @@
#
##############################################################################
import tools
from osv import osv
from osv import fields
from openerp import tools
from openerp.osv import osv, fields
def _reopen(self, res_id, model):
return {'type': 'ir.actions.act_window',
@ -39,7 +36,6 @@ def _reopen(self, res_id, model):
},
}
class mail_compose_message(osv.TransientModel):
_inherit = 'mail.compose.message'
@ -60,37 +56,19 @@ class mail_compose_message(osv.TransientModel):
record_ids = email_template_obj.search(cr, uid, [('model', '=', model)], context=context)
return email_template_obj.name_get(cr, uid, record_ids, context) + [(False, '')]
def default_get(self, cr, uid, fields, context=None):
if context is None:
context = {}
result = super(mail_compose_message, self).default_get(cr, uid, fields, context=context)
result['template_id'] = context.get('default_template_id', context.get('mail.compose.template_id', False))
# pre-render the template if any
if result.get('use_template') and result.get('template_id'):
onchange_res = self.onchange_use_template(cr, uid, [], result.get('use_template'), result.get('template_id'),
result.get('composition_mode'), result.get('model'), result.get('res_id'), context=context)
result.update(onchange_res['value'])
return result
_columns = {
'use_template': fields.boolean('Use Template'),
# incredible hack of the day: size=-1 means we want an int db column instead of an str one
'template_id': fields.selection(_get_templates, 'Template', size=-1),
}
_defaults = {
'use_template': True,
}
def onchange_template_id(self, cr, uid, ids, use_template, template_id, composition_mode, model, res_id, context=None):
""" - use_template not set: return default_get
- use_template set in mass_mailing: we cannot render, so return the template values
- use_template set: return rendered values """
if use_template and template_id and composition_mode == 'mass_mail':
def onchange_template_id(self, cr, uid, ids, template_id, composition_mode, model, res_id, context=None):
""" - mass_mailing: we cannot render, so return the template values
- normal mode: return rendered values """
if template_id and composition_mode == 'mass_mail':
values = self.pool.get('email.template').read(cr, uid, template_id, ['subject', 'body_html'], context)
values.pop('id')
elif use_template and template_id:
elif template_id:
# FIXME odo: change the mail generation to avoid attachment duplication
values = self.generate_email_for_composer(cr, uid, template_id, res_id, context=context)
# transform attachments into attachment_ids
values['attachment_ids'] = []
@ -102,39 +80,16 @@ class mail_compose_message(osv.TransientModel):
'datas_fname': attach_fname,
'res_model': model,
'res_id': res_id,
'type': 'binary', # overwrite the context default_value
'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:
values = self.default_get(cr, uid, ['body', 'body_html', 'subject', 'partner_ids', 'attachment_ids'], context=context)
values = self.default_get(cr, uid, ['body', 'subject', 'partner_ids', 'attachment_ids'], context=context)
if values.get('body_html'):
values['body'] = values.pop('body_html')
values.update(use_template=use_template, template_id=template_id)
return {'value': values}
def toggle_template(self, cr, uid, ids, context=None):
""" hit toggle template mode button: calls onchange_use_template to
emulate an on_change, then writes the values to update the form. """
for record in self.browse(cr, uid, ids, context=context):
onchange_res = self.onchange_use_template(cr, uid, ids, not record.use_template,
record.template_id, record.composition_mode, record.model, record.res_id, context=context).get('value', {})
# update partner_ids and attachment_ids
onchange_res['partner_ids'] = [(4, partner_id) for partner_id in onchange_res.pop('partner_ids', [])]
onchange_res['attachment_ids'] = [(4, attachment_id) for attachment_id in onchange_res.pop('attachment_ids', [])]
record.write(onchange_res)
return _reopen(self, record.id, record.model)
def onchange_use_template(self, cr, uid, ids, use_template, template_id, composition_mode, model, res_id, context=None):
""" onchange_use_template (values: True or False). If use_template is
False, we do as an onchange with template_id False for values """
values = self.onchange_template_id(cr, uid, ids, use_template,
template_id, composition_mode, model, res_id, context=context)
# force html when using templates
if use_template:
values['value']['content_subtype'] = 'html'
return values
def save_as_template(self, cr, uid, ids, context=None):
""" hit save as template button: current form value will be a new
template attached to the current document. """
@ -155,7 +110,7 @@ class mail_compose_message(osv.TransientModel):
'attachment_ids': [(6, 0, [att.id for att in record.attachment_ids])]
}
template_id = email_template.create(cr, uid, values, context=context)
record.write(record.onchange_template_id(True, template_id, record.composition_mode, record.model, record.res_id)['value'])
record.write(record.onchange_template_id(template_id, record.composition_mode, record.model, record.res_id)['value'])
return _reopen(self, record.id, record.model)
#------------------------------------------------------
@ -167,7 +122,7 @@ class mail_compose_message(osv.TransientModel):
mail.compose.message, transform email_cc and email_to into partner_ids """
template_values = self.pool.get('email.template').generate_email(cr, uid, template_id, res_id, context=context)
# filter template values
fields = ['body', 'body_html', 'subject', 'email_to', 'email_recipients', 'email_cc', 'attachments']
fields = ['body_html', 'subject', 'email_to', 'email_recipients', 'email_cc', 'attachments']
values = dict((field, template_values[field]) for field in fields if template_values.get(field))
values['body'] = values.pop('body_html', '')
# transform email_to, email_cc into partner_ids

View File

@ -7,25 +7,17 @@
<field name="model">mail.compose.message</field>
<field name="inherit_id" ref="mail.email_compose_message_wizard_form"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='content_subtype']" position="after">
<field name="use_template" invisible="1"
on_change="onchange_use_template(use_template, template_id, composition_mode, model, res_id, context)"/>
</xpath>
<xpath expr="//footer" position="inside">
<group class="oe_right" col="1">
<div attrs="{'invisible':[('use_template','=',False)]}">Use template
<field name="template_id" attrs="{'invisible':[('use_template','=',False)]}"
nolabel="1"
on_change="onchange_template_id(use_template, template_id, composition_mode, model, res_id, context)"/>
</div>
<button icon="/email_template/static/src/img/email_template_save.png"
type="object" name="save_as_template" string="Save as new template" class="oe_link"
help="Save as a new template"
attrs="{'invisible':[('content_subtype','!=','html')]}"/>
</group>
</xpath>
</data>
<xpath expr="//footer" position="inside">
<group class="oe_right" col="1">
<div>Use template
<field name="template_id" nolabel="1"
on_change="onchange_template_id(template_id, composition_mode, model, res_id, context)"/>
</div>
<button icon="/email_template/static/src/img/email_template_save.png"
type="object" name="save_as_template" string="Save as new template" class="oe_link"
help="Save as a new template"/>
</group>
</xpath>
</field>
</record>

File diff suppressed because one or more lines are too long

View File

@ -356,7 +356,8 @@ class hr_applicant(base_stage, osv.Model):
"""
if isinstance(ids, (str, int, long)):
ids = [ids]
if update_vals is None: vals = {}
if update_vals is None:
update_vals = {}
update_vals.update({
'description': msg.get('body'),
@ -373,12 +374,12 @@ class hr_applicant(base_stage, osv.Model):
}
for line in msg.get('body', '').split('\n'):
line = line.strip()
res = tools.misc.command_re.match(line)
res = tools.command_re.match(line)
if res and maps.get(res.group(1).lower(), False):
key = maps.get(res.group(1).lower())
update_vals[key] = res.group(2).lower()
return super(hr_applicant, self).message_update(cr, uids, ids, update_vals=update_vals, context=context)
return super(hr_applicant, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context)
def create(self, cr, uid, vals, context=None):
obj_id = super(hr_applicant, self).create(cr, uid, vals, context=context)
@ -420,7 +421,7 @@ class hr_applicant(base_stage, osv.Model):
self.write(cr, uid, [applicant.id], {'emp_id': emp_id}, context=context)
self.case_close(cr, uid, [applicant.id], context)
else:
raise osv.except_osv(_('Warning!'),_('You must define Applied Job for this applicant.'))
raise osv.except_osv(_('Warning!'), _('You must define Applied Job for this applicant.'))
action_model, action_id = model_data.get_object_reference(cr, uid, 'hr', 'open_view_employee_list')
dict_act_window = act_window.read(cr, uid, action_id, [])
@ -433,13 +434,13 @@ class hr_applicant(base_stage, osv.Model):
"""Overrides cancel for crm_case for setting probability
"""
res = super(hr_applicant, self).case_cancel(cr, uid, ids, context)
self.write(cr, uid, ids, {'probability' : 0.0})
self.write(cr, uid, ids, {'probability': 0.0})
return res
def case_pending(self, cr, uid, ids, context=None):
"""Marks case as pending"""
res = super(hr_applicant, self).case_pending(cr, uid, ids, context)
self.write(cr, uid, ids, {'probability' : 0.0})
self.write(cr, uid, ids, {'probability': 0.0})
return res
def case_reset(self, cr, uid, ids, context=None):
@ -452,7 +453,7 @@ class hr_applicant(base_stage, osv.Model):
def set_priority(self, cr, uid, ids, priority, *args):
"""Set applicant priority
"""
return self.write(cr, uid, ids, {'priority' : priority})
return self.write(cr, uid, ids, {'priority': priority})
def set_high_priority(self, cr, uid, ids, *args):
"""Set applicant priority to high
@ -475,7 +476,7 @@ class hr_applicant(base_stage, osv.Model):
return self.message_post(cr, uid, ids, body=_("Stage changed to <b>%s</b>.") % (stage_name), context=context)
def case_get_note_msg_prefix(self, cr, uid, id, context=None):
return 'Applicant'
return 'Applicant'
def case_open_send_note(self, cr, uid, ids, context=None):
message = _("Applicant has been set <b>in progress</b>.")

View File

@ -65,7 +65,10 @@ Main Features
'mail_alias_view.xml',
'res_users_view.xml',
],
'demo': ['data/mail_demo.xml'],
'demo': [
'data/mail_demo.xml',
'data/mail_group_demo_data.xml',
],
'installable': True,
'auto_install': False,
'application': True,

View File

@ -2,90 +2,354 @@
<openerp>
<data noupdate="1">
<!-- Update demo user to avoid mail bombing -->
<!-- Update 'Demo user' and partners email preferences to avoid mail bombing -->
<record id="base.partner_demo" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<!-- Pushed to all employees -->
<record id="message_blogpost0" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="mail.group_all_employees"/>
<field name="body"><![CDATA[<p>Your monthly meal vouchers arrived. You can get them at Christine's office.</p>]]></field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_root"/>
<record id="base.res_partner_1" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="message_blogpost0_comment0" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="group_all_employees"/>
<field name="body"><![CDATA[<p>Oh, I had forgotten. This month you also get 250 EUR of eco-vouchers if you have been in the company for more than a year.</p>]]></field>
<field name="parent_id" ref="message_blogpost0"/>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_root"/>
<record id="base.res_partner_2" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="message_blogpost0_comment1" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="group_all_employees"/>
<field name="body"><![CDATA[<p>Thanks! Could you please remind me where is Christine's office, if I may ask? I'm new here!</p>]]></field>
<field name="parent_id" ref="message_blogpost0"/>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_demo"/>
<record id="base.res_partner_3" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<!-- This one is starred for having mailboxes with demo data -->
<record id="message_blogpost0_comment2" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="group_all_employees"/>
<field name="body"><![CDATA[<p>Building B3, second floor on the right :-).</p>]]></field>
<field name="parent_id" ref="message_blogpost0"/>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_root"/>
<field name="favorite_user_ids" eval="[(6, 0, [ref('base.user_root'), ref('base.user_demo')])]"/>
<record id="base.res_partner_4" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="message_blogpost0_comment3" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="group_all_employees"/>
<field name="body"><![CDATA[<p>Many thanks. Actually that's good news, next year I'll have to buy a new fridge, I think I will pay it with the eco-vouchers!</p>]]></field>
<field name="parent_id" ref="message_blogpost0"/>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_demo"/>
<record id="base.res_partner_5" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_6" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_7" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_8" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_9" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_10" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_11" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_12" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_13" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_14" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_15" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_16" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_17" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_18" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_19" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_20" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_21" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_22" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<record id="base.res_partner_23" model="res.partner">
<field name="notification_email_send">none</field>
</record>
<!-- Demo user and admin conversation -->
<record id="message_discussion" model="mail.message">
<field name="body">Hello Demo User! I was wondering whether you had some issues with our secret task about putting cats everywhere in OpenERP.</field>
<!-- Discussion: attachments and spec [DEMO: search on has_attachments -->
<record id="msg_discus6_attach1" model="ir.attachment">
<field name="datas">bWlncmF0aW9uIHRlc3Q=</field>
<field name="datas_fname">RedHat_spec.doc</field>
<field name="name">RedHat_spec.doc</field>
</record>
<record id="msg_discus6_attach2" model="ir.attachment">
<field name="datas">bWlncmF0aW9uIHRlc3Q=</field>
<field name="datas_fname">RedHat_spec_draft_v3.doc</field>
<field name="name">RedHat_spec_draft_v3.doc</field>
</record>
<record id="msg_discus6" model="mail.message">
<field name="subject">RedHat server updated spec</field>
<field name="body"><![CDATA[<p>Hello Demo,</p>
<p>We have a lot of inquiries about our now solution based on RedHat servers. However I do not have the updated specification ready at hand.</p>
<p>Could you please send me the last version of the file asap?</p>
<p>Thanks,</p>]]></field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_root"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(days=1)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<record id="message_discussion_answer1" model="mail.message">
<field name="body">No specific issues, I think everything is clear.</field>
<field name="parent_id" ref="message_discussion"/>
<record id="msg_discus6_1" model="mail.message">
<field name="body"><![CDATA[<p>Sure, here it is. Have a nice day!</p>]]></field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="parent_id" ref="msg_discus6"/>
<field name="author_id" ref="base.partner_demo"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_root')])]"/>
<field name="attachment_ids" eval="[(6, 0, [ref('msg_discus6_attach1')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(hours=3)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<record id="message_discussion_answer2" model="mail.message">
<field name="body">Ow, just to be sure... we were talking about lolcats, right ?</field>
<field name="parent_id" ref="message_discussion"/>
<record id="msg_discus6_2" model="mail.message">
<field name="body"><![CDATA[<p>I just found a more recent draft of the spec. Jon did some cleaning in the specifications. Could you merge the two documents to have an updated one?</p><p>When it's done, put it on the internal document management system.</p><p>Thanks,</p>]]></field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="parent_id" ref="msg_discus6"/>
<field name="author_id" ref="base.partner_demo"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_root')])]"/>
<field name="attachment_ids" eval="[(6, 0, [ref('msg_discus6_attach2')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(hours=3)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<record id="message_discussion_answer3" model="mail.message">
<field name="body">Absolutely!</field>
<field name="parent_id" ref="message_discussion"/>
<!-- Thread: 1 incoming email -->
<record id="msg_discus5" model="mail.message">
<field name="subject">Plan to install backup servers</field>
<field name="body"><![CDATA[<p>Hello,</p>
<p>We need to deploy new backup servers, with the following requirements:</p>
<ul>
<li>daily incremental backups, with an history of 15 days,</li>
<li>45 servers should be backuped between 1am and 3am when our offices are closed,</li>
<li>two redundant servers,</li>
<li>total capacity of 200Gb.</li>
</ul>
<p>
Do you have a simple solution with servers running on Redhat Linux?
</p>
<p>Best regards,</p>
]]></field>
<field name="type">email</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="email_from">virginie@agrolait.fr</field>
<field name="author_id" eval="False"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo'), ref('base.partner_root')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(days=2)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<!-- Admin and Demo with attachments -->
<record id="msg_discus4_attach1" model="ir.attachment">
<field name="datas">bWlncmF0aW9uIHRlc3Q=</field>
<field name="datas_fname">catalog.doc</field>
<field name="name">catalog.doc</field>
</record>
<record id="msg_discus4_attach2" model="ir.attachment">
<field name="datas_fname">activity_graph_2012.jpg</field>
<field name="name">activity_graph_2012</field>
<field name="datas">
/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEP
ERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4e
Hh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCABQAGQDAREA
AhEBAxEB/8QAHAABAAIDAQEBAAAAAAAAAAAABgUHAAMEAgEI/8QATBAAAQIEAQYHCA8GBwAAAAAA
AgMEAAEFBhIHERMis7QUMjQ2QnN0IzE3UnJ1ssIVFhchNVRiY3GCkpOxw9IkJiczQVFDVWGDosHi
/8QAGwEAAgMBAQEAAAAAAAAAAAAABQYABAcCAwH/xABAEQABAgMDCAgDBQcFAAAAAAAAAgMEBQYB
EjUHFDIzNEJy8BMiMVJxssLSESE2QXORksEVIySBgqKzobHD0eL/2gAMAwEAAhEDEQA/AP1mLpMl
VgTTWM0VNGeAOlgx/gcLyIVa9A9jdwhT4q7+7jvMXjjpLDOET+Kr/dxMxePvSWHFUlHq1McpU8VE
HhoqC3WJHGAKYNQ/ojtEE8fL4YUbZRUJKptXtCSQkOFGRiusefSZwmZ4NfUwDHvmJL5uFvfKmAVn
9NMBXxzworAZBp0D8TxAXl9cImauEv2HK3bZTBNA3VUoa2jRQkp+zLBjXkYabocTR6bB8vBEzQl8
8IoZTQQMlajQl19CpLOLVbMS+OeDocQA/wCYS8f3pmJL50PE8oarkJN6hRUGoKOcaajZYzMNJ3Do
eJx/LiZoS+eE0MoxOFDXqNGkhN2iphSbLYxRDBpA4nTzH9uJmhL424RP4qv93FXMXj70lhnCJ/FV
/u4mYvE6SwxiuDpElUJywyUNOf0gUxn6MeDram1XbToMySBzcFfTV0EiDQ8G0xamkNH/AMB9iLyF
rQwi4ch26nziiSeBNtSFDBuayEhTWPH4nT8uJna2+us83+o2taCxLYWNa2qY4U45tUzP7EE0aBCU
xR6EMxRCGYohDMUQhmKIQzFEIZiiEMxRCA6/brcW1JnJCmpPFHMjmUzc6GQCGHP0Dz8eAU4niJYt
pC0X74Slsrz2+qxegTFu8iX7a625xIvW2lBHYRk6DTay9q0n6BGPDUZywnh4iAYPTOLkKhDjHXJa
bDsm31GugNqsYaPQz7uesH+se+aMrJY4tBPsGyTFkiyaBIEUAAA8mLRwdUQh814hDNeIQ+xCEa+q
1OYOWzR48RQWclgQAz1jnHaELVYea30IVcUSOvHB6AyqXgjp6tT2TdQH7FssumawZkVME5B3/LOK
81WuXwK4s5l7jMbG5qebdrtUqOT064vwOVQGTmUpAmWhxJrGAauLP0P7x4yCJXMYZl1fas950hEv
UtKNwF3dUnlVt2jvn+h4SZVBM9EMxDua4B6kKtet9DHQqEd/2ByjXlvQL6186ZaFu8iX7a625wcj
dbaAUdh6ofLqz24NghF6B1ZFkxF44IR3X6a2rTajrrkLxzLuI6IsJTzHPjZs3eAo7QytaL5XXFNp
c6HfJuOCwBLEq9WqFx1xnUHvCkEQQWQDQCGgxmvqZ+n7wBF+Oh0MJRcBEtilvrXfJq+iVC2nJorK
InIk8xongOWuEeECi+/ZYsszJa0Qq7UH2xjWVtGmKLrKLmSGczWPEZxzFWWWPLssO4K2+wi0OZRO
dFM6+n76EXYXZVg2O25s92pXKs6ygVmmPH2lZoAoaCOgEcGZXBx5d+EKSzSIjY+IZXoIHiYwbDEE
y4jTWQDrnTcPm57t04ZqswJfAK1O42TNleCRTy328rQNonD4YN1brnvD0BGs8yqN19U3qAeUPEoX
j9heobDn+e+W1bvIl+2utucFo3W2gdHYGbrJYGFV0DpdqZ1YO6IngPkqcHJGhC7euC5y4tDHUEln
qLOLSo66ypGsqwQMz8YppynOcdv6ay0xqUBq7/CZb/Xh6C8EWNhWCn8RQP8AB8soFhwrfJnLFd9x
dkZbR1BeZaLYvSfWPc98UX4H7sONc/5iG2CKMFr7AhNdkWV0b1+2O00WzxdFGfBcQAeopI3UgPP9
TPCVUcwimKgZh23OosapBCsLkq1qR8/h6BJlC51Uvr2W+pw/wuyrEiO25s5bK8KVe6lXbxl9NYtG
8a/MaLOcMY53COec8bh81vdsEPNXYEvgEmnMcs53yYs7wRq+W+3laBlE4dDBurdc94egI1nmVRuv
qm9QEyh4lC8fsL9DYc/z3y2rd5Ev211tzgtG620Do7A3dnIal53DdU4PU/2gie7OILIEfabQsX+X
IegMR/TWXIXUoDl3+Ey3+vD0F4JMbCsFv4igfYAgWHCuMnUxTuy4jVLCINWWv/uOoKTLRQAJPrnO
e+Jr30J2quYHiAlENfH88EVJdr0F6a7Ksrd1yq0Pop+/SjPao+q4fn7VjhTuBK8LfIKcoQ/vVS5/
Pst9TjTIXZViFHbc2ctleFKvdSrt4y+msWjeNfmNFnOGMc7hHPOdtxebHu2CHmrsCXwCTTmOWc75
LWWP8I1ZfLfbytA2isPhg5Vuue8PQE6zzKo3X1TeoB5Q8SheP2F6hsOf575bVu8iX7a625wWjdba
B0dgbu/kNS87huqcH6f7QRPdQc+R6tu6lSVWDtBqAU5FFFEwH3z1MGt9iF2Rzdc4vvLQBKSnr03Y
X0iNA1325bsb6ojp0uCLcFkiMz94A1F4a1RbENL1reXcLkyfZhI1Djy7iBhR6zRqvpfY162c6HDj
0c5TwQIhI5iKs/crvjQ5DuIQha0aZVwgCyN3gYAYGhT9Q+1Lx8ygW9HJF89wpUPii+e+JmogOSGn
ag/ykNsESltmY4P0LFV6x/jDDrlVofRT9+lClVH1XD8/asP07gSvC3yCbKDh9tVKwcXTst9TjTIX
ZViFHbc2aLKw+6jXM2DDoD20ZdTOLRvGvzmjTnDWOdwjXmf213F4nsW926cPVW4GvgEinsbJa0cP
uRnxcWke7yrAyidghg5Vuuf8PQFKzzKo3X1TeoCZQ8SheP2F6hsOf575bVu8iX7a625wWjdbaB0d
gbuvkVS87huqcH6f7QRPdQFsitXY0528YOlCTWfqIg2zjx8wnGbUbGsotXDL01i1k4l0SqWPRNiO
pfOjLXrPx8hL8yGassET956FgvKFqPyf8hIZHfhSudS1/FaFnJ9oP/0G71T2sA2rvXLetuWyRSBF
8CILBg42AzMPTgplHmr7dyAs0Ll78V/+DK6WmrzNWMwSdWu/5Fj9v4Iaf1SG1GGildmh+D9BqqvW
P8YLk+bO3FuJomZm0WZILTwf4nDU4Rp/GsxNTsKZV8k/L8L4To+OYipG90O5fR/YM8oXOql9ey31
ONWhdlWJ0dtzZzWT4Uq51J7eMvpnFo3jX5zSJzhrHO4RjznXcXmx7t04eauwJfAI1PY2S9ol/CM5
/OPd5VgZROwQwcqzXP8Ah6AnWeZVG6+qb1ATKHiULx+wvUNhz/PfLbt/ka/bXW3OC8ZrbQOjsDV2
chqXncN1Tg7T/aCJ7s5XOT7nXQ/OP5KkYrTOLtnWS/6Qf41/7NibLX8Ip+QHrxpNZYIn7z0LEPKH
qPyes7sjvwtXOqa/itCzk+1b/ij9TeKp7WP5gi4ecqH0fmHHGUzEEcHrWY3Tf1xBcC/8bhY7XwPs
eqQ2wQ/UlqoXg/Qeqn03+P1lb0L4ca+eWW9BGRMfUTfGUMl2Axn3i/8AGgZ5XpqA5FwkaiSwJIqJ
GHRMFsca9OYl2DkrrzOnZ70CzVby4ZpbzNvXSizz2HNkfNVS7HSqpmqopTsahl0j00Z/Q7jj8U+t
Zp6nlvSCCWvuI8hw3FUAp111XSAZ8LQdNk8HjmsH6Icq3mjENLLIZemtAhyeaswNQssL31iayvBI
p5b7eVo+0Th8MN9W657w9ARrPMqjdfVN6gHlDxKF4/YXqGw5/nvlt2/yNftrrbnBWL1toIsD1zUy
puAepMmILiu9ByB6cA7yIB6kE5VHIhdMoTKFXFN3EA+2bPuOm1ynPV6egYtnM1jwOg72Aw9eEKTS
B2BjERC1/I4o+DVJZI5AP6a13/J/0S9+0S4Lhcpm1paaWEA47oP6aT9cNM9SmZS3NW+/fAVVSB6b
JsQyvnrnbYVKrtvun7h1SgPhKaIJiDoOhj/XAemZZ+yEL6demaLOZi3G2otQG6paFzOquLkacgCY
5s4E6Cf949KrlypzEodh17l3n8RDlUnegqiZmji+oi//AKoX7xSDGtJ2M3oPsR3YQAJnwoOgeP8A
6hgkkS3L22W3NwYpz/GrXc3wpS7Oudu+RXUpyE003qLif7SHQXA/UhLRIHUTRuMv9S/f/H4njR6H
JNK4mGf01rWv8yPgTl90ivV6eFrSQDuIJ67kP6HjhymzqI2WOwbemv3oAdRSZ+YQ622973/E1WDQ
6/blTXeuaYBaRsCAADoO/jzwu05KrZWta316Y3JdsslUNB76EIR+RBw3LatyVSrk9QpaAJzWUPWd
B0zi1VkNbObGbGF6HyE6yRv2TuGj7/UQu+IaBTq7TrIOhHSBNY9PPGLoOmsZ+vBKQOIl8My05uDb
O1WR63FN74OlZFz8JXMmTfQLGZAPDeJjOFmZyeKi5hnC3upfvguj2XpIuNcinL/TXLn9/vLUpDNw
kxzLdwM111ZhJXPm0ixn3/rQxvK6Vd4vn//Z
</field>
</record>
<record id="msg_discus4" model="mail.message">
<field name="body"><![CDATA[<p>Hi Demo,</p>
<p>The next version of our products catalog is scheduled for next month. Our product team send me their updated document holding the prices and costs, and I updated our catalog.</p>
<p>You will find it in attachment, as well as a comparative benchmark of the different solutions currently existing on the market.<br />Have a nice reading!<br />
Sincerely,</p>]]></field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_root"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo')])]"/>
<field name="attachment_ids" eval="[(6, 0, [ref('msg_discus4_attach1'), ref('msg_discus4_attach2')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(days=1)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<record id="msg_discuss4_1" model="mail.message">
<field name="body"><![CDATA[<p>Thank you!<br/>Could you send me the updated pricelists as negotiated at the beginning of this year?</p>
<p>Sincerely,</p>]]></field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="parent_id" ref="msg_discus4"/>
<field name="author_id" ref="base.partner_demo"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_root'), ref('base.partner_demo')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(hours=1, minutes=30)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<!-- Thread: Demo (network admin) and Admin -->
<record id="msg_discus3" model="mail.message">
<field name="body"><![CDATA[<p>Hello,</p><p>I have a friend working at Epic Technologies. He told me they plan to upgrade their backup servers within the next 3 months.</p><p>I think that someone should contact them and check if there is an opportunity.</p>]]></field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_demo"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_root')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(hours=3)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<record id="msg_discus3_1" model="mail.message">
<field name="body"><![CDATA[Contact Chris: +1 (650) 307-6736.]]></field>
<field name="parent_id" ref="msg_discus3"/>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_demo"/>
<field name="notified_partner_ids" eval="[(6, 0, [ref('base.partner_root')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(hours=1)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<!-- Thread: Epic Technologies and Admin as salesman discuss about a meeting [DEMO: see context, mark thread as done] -->
<record id="msg_discus2" model="mail.message">
<field name="subject">Information meeting</field>
<field name="body"><![CDATA[<p>Hello,</p><p>Epic Technologies is a small company specialized in software managing huge volume of data. Having an efficient and reliable backup system is very important for us, and critical for our customers. I eared you have some interesting solutions to manage our backups. Could we meet each other as soon as possible to discuss our need? Here is a first list of requirements:</p>
<ul>
<li>about 25 backup servers, running on Redhat Linux</li>
<li>minimum 200Gb of storage per server</li>
<li>setup a VPN between all our servers in New York and Chicago</li>
</ul>
<p>Thanks,</p>
]]></field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.res_partner_5"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_root')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(days=1)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<record id="msg_discus2_1" model="mail.message">
<field name="subject">RE: Information meeting</field>
<field name="body"><![CDATA[<p>Hello Epic!</p>
<p>I am glad you are interested in our products. Indeed, we are have several backup solutions that should meet your requirements. In order to prepare a detailed offer, we will have to discuss several technical points about your needs and the context of your data management.</p>
<p>I propose to have a meeting tomorrow at 2 PM. Does it seem suitable for you ?<br />Best regards,</p>]]></field>
<field name="parent_id" ref="msg_discus2"/>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_root"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.res_partner_5')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(hours=1)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<record id="msg_discus2_2" model="mail.message">
<field name="subject">RE: Information meeting</field>
<field name="body"><![CDATA[<p>It is not possible for me to come tomorrow at 2 PM. Maybe at 4 PM?</p>]]></field>
<field name="parent_id" ref="msg_discus2"/>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.res_partner_5"/>
<field name="date" eval="(DateTime.today() - timedelta(minutes=35)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<record id="msg_discus2_3" model="mail.message">
<field name="subject">RE: Information meeting</field>
<field name="body"><![CDATA[<p>4 PM is fine! See you tomorrow!<br />Best regards,</p>]]></field>
<field name="parent_id" ref="msg_discus2"/>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_root"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.res_partner_5')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(minutes=30)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<record id="msg_discus2_4" model="mail.message">
<field name="subject">RE: Information meeting</field>
<field name="body"><![CDATA[<p>Ok! See you tomorrow.</p>]]></field>
<field name="parent_id" ref="msg_discus2"/>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.res_partner_5"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_root')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(minutes=10)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<!-- Short thread: Admin ask, Agrolait answer [DEMO: mark thread as done] -->
<record id="msg_discus1" model="mail.message">
<field name="subject">Feedback about our On Site Assistance</field>
<field name="body"><![CDATA[<p>Hi Virginie,</p><p>I writing to you about our <i>On Site Assistance Service</i> that we delivered to Agrolait last week. Do you have any feedback or remark about our service? I noticed you requested new IP phones. Will it be used for new employees, or did you have any issue with the ones we provided?<br />Best regards,</p>]]></field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_root"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.res_partner_2')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(days=2)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<record id="msg_discus1_1" model="mail.message">
<field name="subject">RE: Feedback about our On Site Assistance</field>
<field name="body"><![CDATA[<p>Hello Administrator,</p><p>Glad to hearing from you! Everything is perfect, thanks for asking. Concerning the order of 2 IP phones, I ordered them for new employees. We are satisfied with the products of <i>YourCompany</i>, and we plan to fit out each new employee with one of your phone this year.<br />Regards,</p>]]></field>
<field name="parent_id" ref="msg_discus1"/>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.res_partner_2"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_root')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(minutes=5)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<!-- Header only: message from res_partner_1 [DEMO: mark as done] -->
<record id="msg_discus0" model="mail.message">
<field name="subject">FWD: Meeting with Demo </field>
<field name="body"><![CDATA[<p>Hello Administrator,</p><p>A small email to inform you that we will have a meeting with Mr Demo next Tuesday. Everything is under control!<br />Regards,</p>]]></field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.res_partner_1"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_root')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(minutes=1)).strftime('%Y-%m-%d %H:%M')"/>
</record>
</data>

View File

@ -2,10 +2,6 @@
<openerp>
<data noupdate="1">
<record model="mail.group" id="group_sales">
<field name="name">Sales</field>
<field name="description">Discussion about best sales practices and deals.</field>
</record>
<record model="mail.group" id="group_all_employees">
<field name="name">Whole Company</field>
<field name="group_ids" eval="[(4, ref('base.group_user'))]"/>
@ -19,9 +15,9 @@
<field name="type">notification</field>
<field name="subtype_id" ref="mail.mt_comment"/>
<field name="subject">Welcome to OpenERP!</field>
<field name="body"><![CDATA[<p>Your homepage is a summary of messages you received and key information about documents you follow.</p><p>
The top menu bar contains all applications you installed. You can use the <i>Settings</i> menu to install more applications, activate others features or give access to new users.</p><p>
To setup your preferences (name, email signature, avatar), click on the top right corner.</p>]]></field>
<field name="body"><![CDATA[<p>Your homepage is a summary of messages you received and key information about documents you follow.</p>
<p>The top menu bar contains all applications you installed. You can use the <i>Settings</i> menu to install more applications, activate others features or give access to new users.</p>
<p>To setup your preferences (name, email signature, avatar), click on the top right corner.</p>]]></field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,197 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<!-- Discussion groups, done in 2 steps to remove creator from followers -->
<record model="mail.group" id="group_best_sales_practices">
<field name="name">Best Sales Practices</field>
<field name="description">Discussion about best sales practices and deals.</field>
</record>
<record model="mail.group" id="group_best_sales_practices">
<field name="message_follower_ids" eval="[(6, 0, [ref('base.partner_demo')])]"/>
</record>
<record model="mail.group" id="group_board">
<field name="name">Board meetings</field>
<field name="description">Board meetings, budgets, strategic plans</field>
</record>
<record model="mail.group" id="group_board">
<field name="message_follower_ids" eval="[(6, 0, [])]"/>
</record>
<record model="mail.group" id="group_rd">
<field name="name">R&amp;D</field>
<field name="description">Research and development discussion group</field>
</record>
<record model="mail.group" id="group_hr_policies">
<field name="name">HR Policies</field>
<field name="description">Company cars, holidays and other advantages</field>
</record>
<record model="mail.group" id="group_rd">
<field name="message_follower_ids" eval="[(6, 0, [])]"/>
</record>
<record model="mail.group" id="group_support">
<field name="name">Support</field>
<field name="description">Support team</field>
</record>
<!-- Best sales practices messages -->
<record id="msg_group_1_1" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="mail.group_best_sales_practices"/>
<field name="body"><![CDATA[<p>Selling a training session and selling the products after the training session is more efficient than directly selling a pack with the training session and the products.</p>]]></field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_demo"/>
<field name="date" eval="(DateTime.today() - timedelta(days=5)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<record id="msg_group_1_2" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="mail.group_best_sales_practices"/>
<field name="body"><![CDATA[<p>I noted I can not manage efficiently my pipeline when I have more than 50 opportunities in the qualification stage.</p><p>Any advice on this? How do you organize your activities with more than 50 opportunities?</p>]]></field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_root"/>
<field name="date" eval="(DateTime.today() - timedelta(days=4)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<record id="msg_group_1_3" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="mail.group_best_sales_practices"/>
<field name="body"><![CDATA[<p>When I have too much opportunities in the pipe, I start communicating with prospects more by email than phonecalls.</p><p>I send an email to create a sense of emergency, like <i>"can I call you this week about our quote?"</i> and I call only those that answer this email.</p><p>You can use the email template feature of OpenERP to automate email composition.</p>]]></field>
<field name="type">comment</field>
<field name="parent_id" ref="msg_group_1_2"/>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_demo"/>
<field name="vote_user_ids" eval="[(6, 0, [ref('base.user_demo')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(days=3)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<record id="msg_group_1_4" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="mail.group_best_sales_practices"/>
<field name="body"><![CDATA[<p>When you sell a tablet PC, don't forget to propose a docking station with it. I offer 20% on the docking stating (not the tablet) and I have a 70% success rate with this combo.</p>]]></field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_demo"/>
<field name="vote_user_ids" eval="[(6, 0, [ref('base.user_demo'), ref('base.user_root')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(days=2)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<!-- Pushed to all employees -->
<record id="msg_empl_1" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="mail.group_hr_policies"/>
<field name="body"><![CDATA[<p>Your monthly meal vouchers arrived. You can get them at the HR's office.</p>]]></field>
<field name="type">comment</field>
<field name="author_id" ref="base.partner_demo"/>
<field name="date" eval="(DateTime.today() - timedelta(hours=1)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<record id="msg_empl_1_1" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="group_hr_policies"/>
<field name="body"><![CDATA[<p>Oh, I had forgotten. This month you also get 250 EUR of eco-vouchers if you have been in the company for more than a year.</p>]]></field>
<field name="parent_id" ref="msg_empl_1"/>
<field name="type">comment</field>
<field name="author_id" ref="base.partner_demo"/>
<field name="date" eval="(DateTime.today() - timedelta(minutes=57)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<record id="msg_empl_1_2" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="group_hr_policies"/>
<field name="body"><![CDATA[<p>Thanks! Could you please remind me where is Christine's office, if I may ask? I'm new here!</p>]]></field>
<field name="parent_id" ref="msg_empl_1"/>
<field name="type">comment</field>
<field name="author_id" ref="base.partner_root"/>
<field name="date" eval="(DateTime.today() - timedelta(minutes=34)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<record id="msg_empl_1_3" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="group_hr_policies"/>
<field name="body"><![CDATA[<p>Building B3, second floor on the right :-).</p>]]></field>
<field name="parent_id" ref="msg_empl_1"/>
<field name="type">comment</field>
<field name="author_id" ref="base.partner_demo"/>
<field name="date" eval="(DateTime.today() - timedelta(minutes=22)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<!-- Board messages -->
<record id="msg_board_1" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="mail.group_board"/>
<field name="body"><![CDATA[
<p>
Dear Board Members,
</p>
<p>
The main events of the month of October are:
</p>
<p>
<b>Sales:</b>
</p>
<ul>
<li>Invoicing is respectively of 442k€ for our European company (66% of the budget) and $404k for the U.S. office (75% of the budget). Despite these numbers that are far bellow our initial expectations, the growth of the month of October is 51% compared to last year.</li>
<li>The month of September having been better than our initial forecasts, the consolidated yearly revenue is only of $20k bellow our forecast made during the board of September.</li>
<li>The consolidated forecast for the end of the year is $6.749k, which is a growth of 76% compared to last year and an achievement of 87% of the budget.</li>
<li>The recruitment of new resellers has been very good, especially in Europe, where we signed 30 new resellers this month.</li>
</ul>
<p>
<b>Finance :</b>
</p>
<ul>
<li>The profit and loss has been negatively impacted this month by revenues that are far beyond the budget and charges that are 15% above the budget. The main extra we had in our charges this month is due to the provisioning of the salaries for the holidays period, $50k.</li>
<li>We also got the payment of our long awaited subsidies, the cash level has increased of 300K€ which gives a current balance of 963 K€ without including the straight loan of 350 K€.</li>
<li>The aged customer balance has been similar to the one of the last month with a small decrease of the DSO. We have recruited a new accountant assistant for the credit collection. She is mostly doing phone calls for all invoices that are due since 30 days, so we should get improvements of the DSO in November. The sum of the invoicing on which we have a risk in the aged customer balance is 100K€.</li>
</ul>
<p>
<b>Resellers and Customers:</b>
</p>
<ul>
<li>The total number of resellers is 429, across 87 countries.</li>
<li>The total number of installations of our software increased to 37K, against 33K for the month of September but we still did not reached the highest level we reached during this year (44K in march and may)</li>
<li>We have passed the 10000th customer in production with 10271 customers at the end of October. The paying customer ratio is 6,6%.</li>
</ul>
<p>
<b>Launch of the new release:</b>
</p>
<p>
We are working actively on the new release which is scheduled for the end of November.
</p>
<ul>
<li>We will publish the release note this week</li>
<li>The whole sales team will be trained on the new version this Friday</li>
<li>We will do a public announce to our resellers the 21th of November. We plan to show them: a description of the new features, the new distribution strategy, the new pricing and the communication plan.</li>
</ul>
<br/>
<p>
Nicolas, can you book a meeting room for our meeting of Friday 2pm?
</p>
<p>
Regards.
</p>
]]></field>
<field name="type">comment</field>
<field name="parent_id" ref="msg_group_1_2"/>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_demo"/>
<field name="vote_user_ids" eval="[(6, 0, [ref('base.user_demo')])]"/>
<field name="date" eval="(DateTime.today() - timedelta(days=3)).strftime('%Y-%m-%d %H:%M')"/>
</record>
<!-- Whole Company messages -->
<record id="msg_whole_1_1" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="mail.group_all_employees"/>
<field name="body"><![CDATA[
<p>
Great news!<br/>
Our company has received the Deloitte Fast 50 award. We are the fastest
growing company of the country, with a growth of 1549% over the past 5
years. You can get more information <a href="http://www.openerp.com/node/1244/2012/10">on our blog</a>.
</p>
]]></field>
<field name="type">comment</field>
<field name="author_id" ref="base.partner_demo"/>
<field name="date" eval="(DateTime.today() - timedelta(minutes=22)).strftime('%Y-%m-%d %H:%M')"/>
</record>
</data>
</openerp>

View File

@ -114,7 +114,7 @@ class mail_group(osv.Model):
alias_id = mail_alias.create_unique_alias(cr, uid,
# Using '+' allows using subaddressing for those who don't
# have a catchall domain setup.
{'alias_name': "group+"+vals['name']},
{'alias_name': "group+" + vals['name']},
model_name=self._name, context=context)
vals['alias_id'] = alias_id
@ -133,6 +133,7 @@ class mail_group(osv.Model):
'context': {'default_model': 'mail.group', 'default_res_id': mail_group_id, 'search_default_message_unread': True},
'res_model': 'mail.message',
'thread_level': 1,
'header_description': vals.get('description'),
}
cobj = self.pool.get('ir.actions.client')
newref = cobj.copy(cr, SUPERUSER_ID, ref[1], default={'params': str(params), 'name': vals['name']}, context=context)
@ -160,6 +161,13 @@ class mail_group(osv.Model):
result = super(mail_group, self).write(cr, uid, ids, vals, context=context)
if vals.get('group_ids'):
self._subscribe_users(cr, uid, ids, context=context)
# if description is changed: update client action
if vals.get('description'):
cobj = self.pool.get('ir.actions.client')
for action in [group.action for group in self.browse(cr, SUPERUSER_ID, ids, context=context) if group.action]:
new_params = action.params
new_params['header_description'] = vals.get('description')
cobj.write(cr, SUPERUSER_ID, [action.id], {'params': str(new_params)}, context=context)
return result
def action_follow(self, cr, uid, ids, context=None):

View File

@ -6,6 +6,17 @@
<field name="name">Discussion Group</field>
<field name="tag">mail.wall</field>
<field name="res_model">mail.message</field>
<field name="context">{
'search_default_message_unread': True
}</field>
<field name="params">{
'read_action': 'read'
}</field>
<field name="help" type="html">
<p>
No message in this group.
</p>
</field>
</record>
<!-- Group Kanban View !-->

View File

@ -59,6 +59,12 @@ class mail_message(osv.Model):
_message_record_name_length = 18
_message_read_more_limit = 1024
def default_get(self, cr, uid, fields, context=None):
# protection for `default_type` values leaking from menu action context (e.g. for invoices)
if context and context.get('default_type') and context.get('default_type') not in self._columns['type'].selection:
context = dict(context, default_type=None)
return super(mail_message, self).default_get(cr, uid, fields, context=context)
def _shorten_name(self, name):
if len(name) <= (self._message_record_name_length + 3):
return name
@ -377,7 +383,9 @@ class mail_message(osv.Model):
id_max = child_id
elif nb > 0:
exp_domain = [('id', '>=', id_min), ('id', '<=', id_max), ('id', 'child_of', message_id)]
messages.append(_get_expandable(exp_domain, nb, message_id, False))
idx = [msg.get('id') for msg in messages].index(child_id) + 1
# messages.append(_get_expandable(exp_domain, nb, message_id, False))
messages.insert(idx, _get_expandable(exp_domain, nb, message_id, False))
id_min, id_max, nb = max(child_ids), 0, 0
else:
id_min, id_max, nb = max(child_ids), 0, 0

View File

@ -79,7 +79,7 @@ class mail_thread(osv.AbstractModel):
# search for unread messages, directly in SQL to improve performances
cr.execute(""" SELECT m.res_id FROM mail_message m
RIGHT JOIN mail_notification n
ON (n.message_id = m.id AND n.partner_id = %s AND n.read = False)
ON (n.message_id = m.id AND n.partner_id = %s AND (n.read = False or n.read IS NULL))
WHERE m.model = %s AND m.res_id in %s""",
(user_pid, self._name, tuple(ids),))
msg_ids = [result[0] for result in cr.fetchall()]
@ -341,7 +341,7 @@ class mail_thread(osv.AbstractModel):
message = self.pool.get('mail.message').browse(cr, uid, message_ids[0], context=context)
_logger.debug('Routing mail with Message-Id %s: direct reply to a private message: %s, custom_values: %s, uid: %s',
message_id, message.id, custom_values, uid)
return [(False, 0, custom_values, uid)]
return [(message.model, message.res_id, custom_values, uid)]
# 2. Look for a matching mail.alias entry
# Delivered-To is a safe bet in most modern MTAs, but we have to fallback on To + Cc values
@ -767,13 +767,19 @@ class mail_thread(osv.AbstractModel):
# 3. Post-processing
# HACK TDE FIXME: Chatter: attachments linked to the document (not done JS-side), load the message
if attachment_ids:
# TDE FIXME (?): when posting a private message, we use mail.thread as a model
# However, attaching doc to mail.thread is not possible, mail.thread does not have any table
model = self._name
if model == 'mail.thread':
model = False
filtered_attachment_ids = ir_attachment.search(cr, SUPERUSER_ID, [
('res_model', '=', 'mail.compose.message'),
('res_id', '=', 0),
('create_uid', '=', uid),
('id', 'in', attachment_ids)], context=context)
if filtered_attachment_ids:
ir_attachment.write(cr, SUPERUSER_ID, attachment_ids, {'res_model': self._name, 'res_id': thread_id}, context=context)
if thread_id and model:
ir_attachment.write(cr, SUPERUSER_ID, attachment_ids, {'res_model': model, 'res_id': thread_id}, context=context)
mail_message.write(cr, SUPERUSER_ID, [new_message_id], {'attachment_ids': [(6, 0, [pid for pid in attachment_ids])]}, context=context)
return new_message_id

View File

@ -84,29 +84,11 @@
'search_default_message_read': True
}</field>
<field name="params" eval="&quot;{
'domain': [('notification_ids.partner_id.user_ids', 'in', [uid])],
'view_mailbox': True,
'read_action': 'unread', }&quot;"/>
<field name="help" type="html">
<p>
No message found.
</p>
</field>
</record>
<record id="action_mail_sent_feeds" model="ir.actions.client">
<field name="name">Sent</field>
<field name="tag">mail.wall</field>
<field name="context">{
'default_model': 'res.users',
'default_res_id': uid
}</field>
<field name="params" eval="&quot;{
'domain': [('author_id.user_ids', 'in', [uid])],
'domain': ['|', ('notification_ids.partner_id.user_ids', 'in', [uid]), ('author_id.user_ids', 'in', [uid])],
'view_mailbox': True, }&quot;"/>
<field name="help" type="html">
<p>
<b>No message sent yet.</b>
No message found and no message sent yet.
</p><p>
Click on the top-right icon to compose a message. This
message will be sent by email if it's an internal contact.
@ -150,11 +132,5 @@
<field name="action" ref="action_mail_archives_feeds"/>
<field name="parent_id" ref="mail.mail_feeds"/>
</record>
<record id="mail_sentfeeds" model="ir.ui.menu">
<field name="name">Sent</field>
<field name="sequence" eval="18"/>
<field name="action" ref="action_mail_sent_feeds"/>
<field name="parent_id" ref="mail.mail_feeds"/>
</record>
</data>
</openerp>

View File

@ -220,9 +220,8 @@
/* --------------------- ATTACHMENTS --------------------- */
.openerp .oe_mail .oe_msg_attachment_list{
display: none;
margin-top: 12px;
margin-bottom: 12px;
margin-top: 4px;
margin-bottom: 4px;
}
.openerp .oe_mail .oe_msg_composer .oe_msg_attachment_list{
display: block;
@ -230,7 +229,7 @@
.openerp .oe_mail .oe_attachment{
display: inline-block;
width: 100px;
margin: 2px;
margin: 4px 2px;
min-height: 80px;
position: relative;
border-radius: 3px;
@ -243,7 +242,7 @@
padding: 1px 3px;
margin-top: 50px;
margin-bottom: 5px;
background: rgba(124, 123, 173, 0.13);
background: #F4F5FA;
overflow: hidden;
color: #4c4c4c;
text-shadow: none;
@ -299,12 +298,13 @@
.openerp .oe_mail .oe_attachment.oe_preview .oe_name{
position: absolute;
bottom: 0px;
margin: 0px;
margin: 3px;
left: 0px;
right: 0px;
max-height: 64px;
background: rgba(0,0,0,0.8);
color: white;
border-radius: 1px;
border-top-left-radius: 0px;
border-top-right-radius: 0px;
opacity: 0;
@ -333,9 +333,11 @@
position: relative;
margin:0px;
width: 100px;
height: 100px;
border-radius: 3px;
height: 80px;
border-radius: 1px;
border: solid 3px #FFF;
margin-left: -50px;
box-shadow: 0px 3px 10px rgba(0, 0, 0, 0.19);
}
.openerp .oe_mail .oe_attachment .oe_delete{
display: none;

View File

@ -7,39 +7,6 @@ openerp.mail = function (session) {
openerp_mail_followers(session, mail); // import mail_followers.js
openerp_FieldMany2ManyTagsEmail(session); // import manyy2many_tags_email.js
/**
* ------------------------------------------------------------
* FormView
* ------------------------------------------------------------
*
* Override of formview do_action method, to catch all return action about
* mail.compose.message. The purpose is to bind 'Send by e-mail' buttons.
*/
session.web.FormView = session.web.FormView.extend({
do_action: function (action) {
if (action.res_model == 'mail.compose.message') {
/* hack for stop context propagation of wrong value
* delete this hack when a global method to clean context is create
*/
var context_keys = ['default_template_id', 'default_composition_mode',
'default_use_template', 'default_partner_ids', 'default_model',
'default_res_id', 'default_content_subtype', 'default_subject',
'default_body', 'active_id', 'lang', 'bin_raw', 'tz',
'active_model', 'edi_web_url_view', 'active_ids',
'default_attachment_ids']
for (var key in action.context) {
if (_.indexOf(context_keys, key) == -1) {
action.context[key] = null;
}
}
/* end hack */
}
return this._super.apply(this, arguments);
},
});
/**
* ------------------------------------------------------------
* ChatterUtils
@ -251,7 +218,7 @@ openerp.mail = function (session) {
this.format_data();
// record options and data
this.show_record_name = this.record_name && !this.thread_level && this.model != 'res.partner';
this.show_record_name = this.options.show_record_name && this.record_name && !this.thread_level && this.model != 'res.partner';
this.options.show_read = false;
this.options.show_unread = false;
if (this.options.show_read_unread_button) {
@ -591,17 +558,17 @@ openerp.mail = function (session) {
//session.web.blockUI();
this.parent_thread.ds_thread.call('message_post_user_api', [
this.context.default_res_id,
mail.ChatterUtils.get_text2html(body),
body,
false,
this.context.default_parent_id,
attachments,
this.parent_thread.context
]).done(function (record) {
var thread = self.parent_thread;
var root = thread == self.options.root_thread;
if (self.options.display_indented_thread < self.thread_level && thread.parent_message) {
var thread = thread.parent_message.parent_thread;
}
var root = thread == self.options.root_thread;
// create object and attach to the thread object
thread.message_fetch([['id', 'child_of', [self.id]]], false, [record], function (arg, data) {
var message = thread.create_message_object( data[0] );
@ -734,7 +701,7 @@ openerp.mail = function (session) {
if(this.thread_level < this.options.display_indented_thread) {
this.create_thread();
}
this.$('.oe_msg_attachments, .oe_msg_images').addClass("oe_hidden");
this.display_attachments();
this.ds_notification = new session.web.DataSetSearch(this, 'mail.notification');
this.ds_message = new session.web.DataSetSearch(this, 'mail.message');
@ -752,8 +719,6 @@ openerp.mail = function (session) {
this.$('.oe_reply').on('click', this.on_message_reply);
this.$('.oe_star').on('click', this.on_star);
this.$('.oe_msg_vote').on('click', this.on_vote);
this.$('.oe_view_attachments').on('click', this.on_view_attachments);
},
/* Call the on_compose_message on the thread of this message. */
@ -812,17 +777,6 @@ openerp.mail = function (session) {
}
},
/* Call the on_compose_message on the thread of this message. */
on_view_attachments:function (event) {
event.stopPropagation();
var self = this;
if (!this.toggle_attachment) {
self.display_attachments();
this.toggle_attachment = true;
}
this.$('.oe_msg_attachment_list').toggle(200);
},
/**
* Wait a confirmation for delete the message on the DB.
* Make an animate destroy
@ -857,7 +811,10 @@ openerp.mail = function (session) {
msg.animated_destroy(150);
} else {
msg.renderElement();
msg.start()
msg.start();
}
if( self.options.root_thread.__parentedParent.__parentedParent.do_reload_menu_emails ) {
self.options.root_thread.__parentedParent.__parentedParent.do_reload_menu_emails();
}
});
@ -997,10 +954,7 @@ openerp.mail = function (session) {
init: function (parent, datasets, options) {
this._super(parent, options);
this.domain = options.domain || [];
this.context = _.extend({
default_model: 'mail.thread',
default_res_id: 0,
default_parent_id: false }, options.context || {});
this.context = _.extend(options.context || {});
this.options = options.options;
this.options.root_thread = (options.options.root_thread != undefined ? options.options.root_thread : this);
@ -1243,6 +1197,7 @@ openerp.mail = function (session) {
'default_parent_id': self.id,
}});
} else {
data.record_name= (data.record_name != '' && data.record_name) || (self.parent_message && self.parent_message.record_name);
var message = new mail.ThreadMessage(self, data, {'context':{
'default_model': data.model,
'default_res_id': data.res_id,
@ -1252,8 +1207,7 @@ openerp.mail = function (session) {
// check if the message is already create
for (var i in self.messages) {
if (self.messages[i] && self.messages[i].id == message.id) {
console.log('Reload message', message.id);
if (message.id && self.messages[i] && self.messages[i].id == message.id) {
self.messages[i].destroy();
}
}
@ -1280,10 +1234,9 @@ openerp.mail = function (session) {
this.$('.oe_view_nocontent').remove();
if (dom_insert_after) {
message.insertAfter(dom_insert_after);
}if (prepend) {
} else if (prepend) {
message.prependTo(self.$el);
} else {
message.appendTo(self.$el);
@ -1460,6 +1413,7 @@ openerp.mail = function (session) {
'show_record_name' : false,
'show_compose_message' : false,
'show_compact_message' : false,
'compose_placeholder': false,
'view_inbox': false,
'message_ids': undefined,
}, this.action.params);
@ -1524,14 +1478,18 @@ openerp.mail = function (session) {
init: function (parent, node) {
this._super.apply(this, arguments);
this.node = _.clone(node);
this.node.params = _.extend({
'display_indented_thread': -1,
'show_reply_button': false,
'show_read_unread_button': false,
'show_record_name': false,
'show_compact_message': 1,
}, this.node.params);
if (this.node.attrs.placeholder) {
this.node.params.compose_placeholder = this.node.attrs.placeholder;
}
this.domain = this.node.params && this.node.params.domain || [];
},
@ -1603,7 +1561,7 @@ openerp.mail = function (session) {
this.action = _.clone(action);
this.domain = this.action.params.domain || this.action.domain || [];
this.context = this.action.params.context || this.action.context || {};
this.context = _.extend(this.action.params.context || {}, this.action.context || {});
this.defaults = {};
for (var key in this.context) {
@ -1617,6 +1575,7 @@ openerp.mail = function (session) {
'show_reply_button': true,
'show_read_unread_button': true,
'show_compose_message': true,
'show_record_name': true,
'show_compact_message': this.action.params.view_mailbox ? false : 1,
'view_inbox': false,
}, this.action.params);
@ -1629,7 +1588,23 @@ openerp.mail = function (session) {
if (! this.searchview.has_defaults) {
this.message_render();
}
},
/**
* crete an object "related_menu"
* contain the menu widget and the the sub menu related of this wall
*/
do_reload_menu_emails: function () {
var menu = this.__parentedParent.__parentedParent.menu;
// return this.rpc("/web/menu/load", {'menu_id': 100}).done(function(r) {
// _.each(menu.data.data.children, function (val) {
// if (val.id == 100) {
// val.children = _.find(r.data.children, function (r_val) {return r_val.id == 100;}).children;
// }
// });
// var r = menu.data;
// window.setTimeout(function(){menu.do_reload();}, 0);
// });
},
/**

View File

@ -36,22 +36,22 @@ openerp_mail_followers = function(session, mail) {
start: function() {
// use actual_mode property on view to know if the view is in create mode anymore
this.view.on("change:actual_mode", this, this._check_visibility);
this._check_visibility();
this.view.on("change:actual_mode", this, this.on_check_visibility_mode);
this.on_check_visibility_mode();
this.reinit();
this.bind_events();
this._super();
},
on_check_visibility_mode: function () {
this.set({"force_invisible": this.view.get("actual_mode") == "create"});
},
set_value: function(_value) {
this.value = _value;
this._super(_value);
},
_check_visibility: function() {
this.$el.toggle(this.view.get("actual_mode") !== "create");
},
reinit: function() {
this.message_is_follower == undefined;
this.display_buttons();
@ -194,6 +194,7 @@ openerp_mail_followers = function(session, mail) {
display_subtypes:function (data) {
var self = this;
var subtype_list_ul = this.$('.oe_subtype_list');
subtype_list_ul.empty();
var records = data[this.view.datarecord.id || this.view.dataset.ids[0]].message_subtype_data;
_(records).each(function (record, record_name) {
record.name = record_name;

View File

@ -39,8 +39,9 @@
</div>
</div>
<div t-if="widget.show_compact_message and !widget.show_composer" t-attf-class="oe_msg oe_msg_composer_compact #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''}">
<textarea t-if="!widget.options.view_mailbox" class="field_text oe_compact" placeholder="Write to the followers of this docmuent..."/>
<textarea t-if="widget.options.view_mailbox" class="field_text oe_compact" placeholder="Write to my followers..."/>
<textarea t-if="widget.options.compose_placeholder" class="field_text oe_compact" t-att-placeholder="widget.options.compose_placeholder"/>
<textarea t-if="!widget.options.compose_placeholder and !widget.options.view_mailbox" class="field_text oe_compact" placeholder="Write an internal note..."/>
<textarea t-if="!widget.options.compose_placeholder and widget.options.view_mailbox" class="field_text oe_compact" placeholder="Share to my followers..."/>
</div>
<span t-if="!(widget.show_compact_message and !widget.show_composer) and !widget.show_composer" class="oe_placeholder_compose"></span>
</t>
@ -84,7 +85,7 @@
</t>
<t t-if="attachment.filetype === 'webimage'">
<div t-attf-class="oe_attachment oe_preview #{attachment.upload ? 'oe_uploading' : ''}">
<a t-att-href='attachment.url'><img t-att-src="widget.attachments_resize_image(attachment.id, [100,100])"></img></a>
<a t-att-href='attachment.url'><img t-att-src="widget.attachments_resize_image(attachment.id, [100,80])"></img></a>
<div class='oe_delete oe_e'>[</div>
<div class='oe_name'><t t-raw='attachment.name' /></div>
<div class='oe_progress_bar'>
@ -101,7 +102,13 @@
<t t-name="mail.thread.list_recipients">
<div class="oe_mail_list_recipients">
To:
<span t-if="!widget.is_private" class="oe_all_follower">Followers <t t-raw="' of ' + (widget.parent_thread.parent_message.record_name ? '&quot;' + widget.parent_thread.parent_message.record_name + '&quot;' : 'this document')"/></span>
<t t-if="!widget.is_private">
<span class="oe_all_follower">
Followers of
<t t-if="widget.parent_thread.parent_message.record_name" t-raw="'&quot;' + widget.parent_thread.parent_message.record_name + '&quot;'"/>
<t t-if="!widget.parent_thread.parent_message.record_name">this document</t>
</span>
</t>
<t t-if="!widget.is_private and widget.partner_ids.length"> and </t>
<t t-set="inc" t-value="0"/>
<t t-if="widget.partner_ids.length" t-foreach="widget.partner_ids" t-as="partner"><span t-attf-class="oe_partner_follower #{inc>=3?'oe_hidden':''}"><t t-if="inc" t-raw="', '"/><a t-attf-href="#model=res.partner&amp;id=#{partner[0]}"><t t-raw="partner[1]"/></a></span><t t-set="inc" t-value="inc+1"/>
@ -129,9 +136,15 @@
<td colspan="2">
<h2 class="oe_view_title">
<span class="oe_view_title_text">
Email box
<t t-raw="widget.action.name"/>
</span>
</h2>
<t t-if="widget.action.params.header_description">
<br/>
<span class="oe_view_subtitle_text">
<t t-raw="widget.action.params.header_description"/>
</span>
</t>
</td>
<td><div class="oe_view_manager_view_search" t-opentag="true"/></td>
</tr>
@ -192,13 +205,11 @@
</div>
<!-- message itself -->
<div class="oe_msg_content">
<h1 t-if="widget.subject and !widget.thread_level" class="oe_msg_title">
<t t-raw="widget.subject"/>
<h1 t-if="(widget.show_record_name or widget.subject) and !widget.thread_level" class="oe_msg_title">
<a t-if="widget.show_record_name" class="oe_mail_action_model" t-attf-href="#model=#{widget.model}&amp;id=#{widget.res_id}"><t t-raw="widget.record_name"/></a>
<t t-if="widget.subject" t-raw="widget.subject"/>
</h1>
<div class="oe_msg_body">
<t t-if="widget.show_record_name">
<a class="oe_mail_action_model" t-attf-href="#model=#{widget.model}&amp;id=#{widget.res_id}"><t t-raw="widget.record_name"/></a>
</t>
<t t-raw="widget.body"/>
</div>
</div>
@ -209,13 +220,6 @@
<a t-if="widget.author_id" t-attf-href="#model=res.partner&amp;id=#{widget.author_id[0]}"><t t-raw="widget.author_id[1]"/></a>
<span class='oe_subtle'></span>
<span t-att-title="widget.date"><t t-raw="widget.timerelative"/></span>
<t t-if='widget.attachment_ids.length > 0'>
<span class='oe_subtle'></span>
<a class="oe_view_attachments">
<t t-if="widget.attachment_ids.length == 1">1 Attachment</t>
<t t-if="widget.attachment_ids.length > 1"><t t-raw="widget.attachment_ids.length"/> Attachments</t>
</a>
</t>
<span class='oe_subtle'></span>
<t t-call="mail.thread.message.vote"/>
</div>

View File

@ -437,9 +437,8 @@ class test_mail(test_mail_mockup.TestMailMockups):
# Mail data
_subject = 'Pigs'
_body_text = 'Pigs rules'
_msg_reply = 'Re: Pigs'
_msg_body = '<pre>Pigs rules</pre>'
_body = 'Pigs <b>rule</b>'
_reply_subject = 'Re: Pigs'
_attachments = [
{'name': 'First', 'datas_fname': 'first.txt', 'datas': 'My first attachment'.encode('base64')},
{'name': 'Second', 'datas_fname': 'second.txt', 'datas': 'My second attachment'.encode('base64')}
@ -462,9 +461,9 @@ class test_mail(test_mail_mockup.TestMailMockups):
# 1. Comment group_pigs with body_text and subject
compose_id = mail_compose.create(cr, uid,
{'subject': _subject, 'body_text': _body_text, 'partner_ids': [(4, p_c_id), (4, p_d_id)]},
{'subject': _subject, 'body': _body, 'partner_ids': [(4, p_c_id), (4, p_d_id)]},
{'default_composition_mode': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_pigs_id,
'default_content_subtype': 'plaintext'})
'default_content_subtype': 'plaintext'})
compose = mail_compose.browse(cr, uid, compose_id)
# Test: mail.compose.message: composition_mode, model, res_id
self.assertEqual(compose.composition_mode, 'comment', 'mail.compose.message incorrect composition_mode')
@ -476,8 +475,8 @@ class test_mail(test_mail_mockup.TestMailMockups):
group_pigs.refresh()
message = group_pigs.message_ids[0]
# Test: mail.message: subject, body inside pre
self.assertEqual(message.subject, False, 'mail.message incorrect subject')
self.assertEqual(message.body, _msg_body, 'mail.message incorrect body')
self.assertEqual(message.subject, _subject, 'mail.message incorrect subject')
self.assertEqual(message.body, _body, 'mail.message incorrect body')
# Test: mail.message: notified_partner_ids = entries in mail.notification: group_pigs fans (a, b) + mail.compose.message partner_ids (c, d)
msg_pids = [partner.id for partner in message.notified_partner_ids]
test_pids = [p_b_id, p_c_id, p_d_id]
@ -495,13 +494,12 @@ class test_mail(test_mail_mockup.TestMailMockups):
{'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])]},
{'default_composition_mode': 'reply', 'default_model': 'mail.thread', 'default_res_id': self.group_pigs_id, 'default_parent_id': message.id})
compose = mail_compose.browse(cr, uid, compose_id)
# Test: model, res_id, parent_id, content_subtype
# Test: model, res_id, parent_id
self.assertEqual(compose.model, 'mail.group', 'mail.compose.message incorrect model')
self.assertEqual(compose.res_id, self.group_pigs_id, 'mail.compose.message incorrect res_id')
self.assertEqual(compose.parent_id.id, message.id, 'mail.compose.message incorrect parent_id')
self.assertEqual(compose.content_subtype, 'html', 'mail.compose.message incorrect content_subtype')
# Test: mail.message: subject as Re:.., body in html, parent_id
self.assertEqual(compose.subject, _msg_reply, 'mail.message incorrect subject')
self.assertEqual(compose.subject, _reply_subject, 'mail.message incorrect subject')
# self.assertIn('Administrator wrote:<blockquote><pre>Pigs rules</pre></blockquote>', compose.body, 'mail.message body is incorrect')
self.assertEqual(compose.parent_id and compose.parent_id.id, message.id, 'mail.message parent_id incorrect')
# Test: mail.message: attachments
@ -518,8 +516,6 @@ class test_mail(test_mail_mockup.TestMailMockups):
{'default_composition_mode': 'mass_mail', 'default_model': 'mail.group', 'default_res_id': False,
'active_ids': [self.group_pigs_id, group_bird_id]})
compose = mail_compose.browse(cr, uid, compose_id)
# Test: content_subtype is html
self.assertEqual(compose.content_subtype, 'html', 'mail.compose.message content_subtype incorrect')
# 2. Post the comment, get created message for each group
mail_compose.send_mail(cr, uid, [compose_id],

View File

@ -21,7 +21,7 @@
from openerp.addons.mail.tests import test_mail_mockup
from openerp.osv.orm import except_orm
from openerp.tools.misc import mute_logger
from openerp.tools import mute_logger
class test_mail_access_rights(test_mail_mockup.TestMailMockups):
@ -52,6 +52,7 @@ class test_mail_access_rights(test_mail_mockup.TestMailMockups):
self.user_raoul = self.res_users.browse(cr, uid, self.user_raoul_id)
self.partner_raoul_id = self.user_raoul.partner_id.id
@mute_logger('openerp.addons.base.ir.ir_model','openerp.osv.orm')
def test_00_mail_message_search_access_rights(self):
""" Test mail_message search override about access rights. """
cr, uid, group_pigs_id = self.cr, self.uid, self.group_pigs_id
@ -85,7 +86,7 @@ class test_mail_access_rights(test_mail_mockup.TestMailMockups):
msg_ids = self.mail_message.search(cr, uid, [('subject', 'like', '_Test')])
self.assertEqual(set([msg_id1, msg_id2, msg_id3, msg_id4, msg_id5, msg_id6, msg_id7, msg_id8]), set(msg_ids), 'mail_message search failed')
@mute_logger('openerp.osv.orm')
@mute_logger('openerp.addons.base.ir.ir_model','openerp.osv.orm')
def test_05_mail_message_read_access_rights(self):
""" Test basic mail_message read access rights. """
cr, uid = self.cr, self.uid
@ -133,8 +134,7 @@ class test_mail_access_rights(test_mail_mockup.TestMailMockups):
self.assertRaises(except_orm, self.mail_message.read,
cr, user_bert_id, message_id)
@mute_logger('openerp.addons.base.ir.ir_model')
@mute_logger('openerp.osv.orm')
@mute_logger('openerp.addons.base.ir.ir_model','openerp.osv.orm')
def test_10_mail_flow_access_rights(self):
""" Test a Chatter-looks alike flow. """
cr, uid = self.cr, self.uid
@ -182,14 +182,14 @@ class test_mail_access_rights(test_mail_mockup.TestMailMockups):
# Do: Bert create a mail.compose.message record, because he uses the wizard
compose_id = mail_compose.create(cr, user_bert_id,
{'subject': 'Subject', 'body_text': 'Body text', 'partner_ids': []},
{'subject': 'Subject', 'body': 'Body text', 'partner_ids': []},
# {'subject': 'Subject', 'body_text': 'Body text', 'partner_ids': [(4, p_c_id), (4, p_d_id)]},
{'default_composition_mode': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_jobs_id})
mail_compose.send_mail(cr, user_bert_id, [compose_id])
self.user_demo_id = self.registry('ir.model.data').get_object_reference(self.cr, self.uid, 'base', 'user_demo')[1]
compose_id = mail_compose.create(cr, self.user_demo_id,
{'subject': 'Subject', 'body_text': 'Body text', 'partner_ids': []},
{'subject': 'Subject', 'body': 'Body text', 'partner_ids': []},
# {'subject': 'Subject', 'body_text': 'Body text', 'partner_ids': [(4, p_c_id), (4, p_d_id)]},
{'default_composition_mode': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_jobs_id})
mail_compose.send_mail(cr, self.user_demo_id, [compose_id])

View File

@ -81,7 +81,7 @@ class mail_compose_message(osv.TransientModel):
elif composition_mode == 'comment' and model and res_id:
vals = self.get_record_data(cr, uid, model, res_id, context=context)
elif composition_mode == 'mass_mail' and model and active_ids:
vals = {'model': model, 'res_id': res_id, 'content_subtype': 'html'}
vals = {'model': model, 'res_id': res_id}
else:
vals = {'model': model, 'res_id': res_id}
if composition_mode:
@ -106,16 +106,10 @@ class mail_compose_message(osv.TransientModel):
'mail_compose_message_ir_attachments_rel',
'wizard_id', 'attachment_id', 'Attachments'),
'filter_id': fields.many2one('ir.filters', 'Filters'),
'body_text': fields.text('Plain-text Contents'),
'content_subtype': fields.char('Message content subtype', size=32, readonly=1,
help="Type of message, usually 'html' or 'plain', used to select "\
"plain-text or rich-text contents accordingly"),
}
_defaults = {
'composition_mode': 'comment',
'content_subtype': lambda self, cr, uid, ctx={}: 'html',
'body_text': lambda self, cr, uid, ctx={}: False,
'body': lambda self, cr, uid, ctx={}: '',
'subject': lambda self, cr, uid, ctx={}: False,
'partner_ids': lambda self, cr, uid, ctx={}: [],
@ -172,32 +166,9 @@ class mail_compose_message(osv.TransientModel):
'parent_id': message_data.id,
'subject': reply_subject,
'partner_ids': partner_ids,
'content_subtype': 'html',
}
return result
def toggle_content_subtype(self, cr, uid, ids, context=None):
""" toggle content_subtype: calls onchange_formatting to emulate an
on_change, then writes the value to update the form. """
for record in self.browse(cr, uid, ids, context=context):
content_st_new_value = 'plain' if record.content_subtype == 'html' else 'html'
onchange_res = self.onchange_content_subtype(cr, uid, ids, content_st_new_value, record.model, record.res_id, context=context)
self.write(cr, uid, [record.id], onchange_res['value'], context=context)
return True
def onchange_content_subtype(self, cr, uid, ids, value, model, res_id, context=None):
""" This onchange allows to have some specific behavior when switching
between text or html mode. This method can be overridden.
:param values: 'plain' or 'html'
"""
return {'value': {'content_subtype': value}}
def dummy(self, cr, uid, ids, context=None):
""" TDE: defined to have buttons that do basically nothing. It is
currently impossible to have buttons that do nothing special
in views (if type not specified, considered as 'object'). """
return True
#------------------------------------------------------
# Wizard validation and send
#------------------------------------------------------
@ -218,8 +189,8 @@ class mail_compose_message(osv.TransientModel):
for res_id in res_ids:
# default values, according to the wizard options
post_values = {
'subject': wizard.subject if wizard.content_subtype == 'html' else False,
'body': wizard.body if wizard.content_subtype == 'html' else '<pre>%s</pre>' % tools.ustr(wizard.body_text),
'subject': wizard.subject,
'body': wizard.body,
'parent_id': wizard.parent_id and wizard.parent_id.id,
'partner_ids': [(4, partner.id) for partner in wizard.partner_ids],
'attachments': [(attach.datas_fname or attach.name, base64.b64decode(attach.datas)) for attach in wizard.attachment_ids],

View File

@ -12,7 +12,6 @@
<field name="model" invisible="1"/>
<field name="res_id" invisible="1"/>
<field name="parent_id" invisible="1"/>
<field name="content_subtype" invisible="1"/>
<!-- visible wizard -->
<label for="partner_ids" string="Recipients"/>
<div>
@ -25,8 +24,7 @@
<field name="partner_ids" widget="many2many_tags_email" placeholder="Add contacts to notify..."
context="{'force_email':True}" required="1"/>
</div>
<field name="subject" placeholder="Subject..."
attrs="{'invisible':[('content_subtype', '=', 'plain')]}"/>
<field name="subject" placeholder="Subject..."/>
</group>
<field name="body"/>
<field name="attachment_ids" widget="many2many_binary"/>

View File

@ -471,6 +471,7 @@ class Product(osv.osv):
'membership_date_to': fields.date('Date to', help='Date until which membership remains active.'),
}
_sql_constraints = [('membership_date_greater','check(membership_date_to >= membership_date_from)','Error ! Ending Date cannot be set before Beginning Date.')]
_defaults = {
'membership': False,
}

View File

@ -37,7 +37,10 @@ openerp.pad = function(instance) {
}else{
this.content = '<div class="oe_pad_loading">... Loading pad ...</div>';
$.get(value+'/export/html').success(function(data){
self.$('.oe_pad_content').html('<div class="oe_pad_readonly">'+data+'<div>');
groups = /\<\s*body\s*\>(.*?)\<\s*\/body\s*\>/.exec(data);
data = (groups || []).length >= 2 ? groups[1] : '';
self.$('.oe_pad_content').html('<div class="oe_pad_readonly"><div>');
self.$('.oe_pad_readonly').html(data);
}).error(function(){
self.$('.oe_pad_content').text('Unable to load pad');
});

View File

@ -22,6 +22,6 @@
import portal
import mail_mail
import wizard
import acquirer
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -49,8 +49,10 @@ very handy when used in combination with the module 'share'.
'portal_view.xml',
'wizard/portal_wizard_view.xml',
'wizard/share_wizard_view.xml',
'acquirer_view.xml',
],
'demo': ['portal_demo.xml'],
'css': ['static/src/css/portal.css'],
'installable': True,
}

98
addons/portal/acquirer.py Normal file
View File

@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2012-TODAY 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
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import logging
from urllib import quote as quote
from openerp.osv import osv, fields
from openerp.tools.translate import _
from openerp.tools import float_repr
_logger = logging.getLogger(__name__)
try:
from mako.template import Template as MakoTemplate
except ImportError:
_logger.warning("payment_acquirer: mako templates not available, payment acquirer will not work!")
class acquirer(osv.Model):
_name = 'portal.payment.acquirer'
_description = 'Online Payment Acquirer'
_columns = {
'name': fields.char('Name', required=True),
'form_template': fields.text('Payment form template (HTML)', translate=True, required=True),
'visible': fields.boolean('Visible', help="Make this payment acquirer available in portal forms (Customer invoices, etc.)"),
}
_defaults = {
'visible': True,
}
def render(self, cr, uid, id, object, reference, currency, amount, context=None, **kwargs):
""" Renders the form template of the given acquirer as a mako template """
if not isinstance(id, (int,long)):
id = id[0]
this = self.browse(cr, uid, id)
if context is None:
context = {}
try:
i18n_kind = _(object._description) # may fail to translate, but at least we try
result = MakoTemplate(this.form_template).render_unicode(object=object,
reference=reference,
currency=currency,
amount=amount,
kind=i18n_kind,
quote=quote,
# context kw would clash with mako internals
ctx=context,
format_exceptions=True)
return result.strip()
except Exception:
_logger.exception("failed to render mako template value for payment.acquirer %s: %r", this.name, this.form_template)
return
def _wrap_payment_block(self, cr, uid, html_block, amount, currency, context=None):
payment_header = _('Pay safely online')
amount_str = float_repr(amount, self.pool.get('decimal.precision').precision_get(cr, uid, 'Account'))
currency_str = currency.symbol or currency.name
amount = u"%s %s" % ((currency_str, amount_str) if currency.position == 'before' else (amount_str, currency_str))
result = """<div class="payment_acquirers">
<div class="payment_header">
<div class="payment_amount">%s</div>
%s
</div>
%%s
</div>""" % (amount, payment_header)
return result % html_block
def render_payment_block(self, cr, uid, object, reference, currency, amount, context=None, **kwargs):
""" Renders all visible payment acquirer forms for the given rendering context, and
return them wrapped in an appropriate HTML block, ready for direct inclusion
in an OpenERP v7 form view """
acquirer_ids = self.search(cr, uid, [('visible', '=', True)])
if not acquirer_ids:
return
html_forms = []
for this in self.browse(cr, uid, acquirer_ids):
html_forms.append(this.render(object, reference, currency, amount, context=context, **kwargs))
html_block = '\n'.join(filter(None,html_forms))
return self._wrap_payment_block(cr, uid, html_block, amount, currency, context=context)

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="acquirer_form" model="ir.ui.view">
<field name="model">portal.payment.acquirer</field>
<field name="arch" type="xml">
<form string="Payment Acquirer" version="7.0">
<group col="1">
<div class="oe_title">
<label for="name" class="oe_edit_only"/><h1><field name="name"/></h1>
<div class="oe_edit_only"><field name="visible"/><label for="visible"/></div>
</div>
<group string="Form Template">
<div>
<p>
This is an HTML form template to submit a payment through this acquirer.
The template will be rendered with <a href="http://www.makotemplates.org/" target="_blank">Mako</a>, so it may use Mako expressions.
The Mako evaluation context provides:
<ul>
<li>reference: the reference number of the document to pay</li>
<li>kind: the kind of document on which the payment form is rendered (translated to user language, e.g. "Invoice")</li>
<li>currency: the currency record in which the document is issued (e.g. currency.name could be EUR)</li>
<li>amount: the total amount to pay, as a float</li>
<li>object: the document on which the payment form is rendered (usually an invoice or sale order record)</li>
<li>quote(): a method to quote special string character to make them suitable for inclusion in a URL</li>
<li>cr: the current database cursor</li>
<li>uid: the current user id</li>
<li>ctx: the current context dictionary</li>
</ul>
If the template renders to an empty result in a certain context it will be ignored, as if it was inactive.
</p>
</div>
<field name="form_template" nolabel="1" colspan="2"/>
</group>
</group>
</form>
</field>
</record>
<record id="acquirer_list" model="ir.ui.view">
<field name="model">portal.payment.acquirer</field>
<field name="arch" type="xml">
<tree string="Payment Acquirers">
<field name="name"/>
<field name="visible"/>
</tree>
</field>
</record>
<record id="acquirer_search" model="ir.ui.view">
<field name="model">portal.payment.acquirer</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
</search>
</field>
</record>
<!-- Acquirers list action is visible in Invoicing Settings -->
<record model="ir.actions.act_window" id="action_acquirer_list">
<field name="name">Payment Acquirers</field>
<field name="res_model">portal.payment.acquirer</field>
</record>
</data>
</openerp>

View File

@ -19,8 +19,9 @@
#
##############################################################################
from osv import osv
import tools
from openerp.osv import osv
from openerp.tools import append_content_to_html
from openerp.tools.translate import _
class mail_mail(osv.Model):
""" Update of mail_mail class, to add the signin URL to notifications. """
@ -36,5 +37,6 @@ class mail_mail(osv.Model):
if partner:
context = dict(context or {}, signup_valid=True)
partner = self.pool.get('res.partner').browse(cr, uid, partner.id, context)
body = tools.append_content_to_html(body, ("<div><p>Log in our portal at: %s</p></div>" % partner.signup_url), plaintext=False)
text = _("""Access your personal documents through <a href="%s">our Customer Portal</a>""") % partner.signup_url
body = append_content_to_html(body, ("<div><p>%s</p></div>" % text), plaintext=False)
return body

View File

@ -27,6 +27,23 @@
<field name="res_id" ref="company_jobs"/>
<field name="view_mode">form</field>
</record>
<record id="paypal_acquirer" model="portal.payment.acquirer">
<field name="name">Paypal</field>
<field name="form_template"><![CDATA[
% if object.company_id.paypal_account:
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
<input type="hidden" name="cmd" value="_xclick"/>
<input type="hidden" name="business" value="${object.company_id.paypal_account}"/>
<input type="hidden" name="item_name" value="${object.company_id.name} ${kind.title()} ${reference}"/>
<input type="hidden" name="amount" value="${amount}"/>
<input type="hidden" name="currency_code" value="${currency.name}"/>
<input type="image" name="submit" src="https://www.paypal.com/en_US/i/btn/btn_paynowCC_LG.gif"/>
</form>
% endif
]]></field>
</record>
</data>
</openerp>

View File

@ -60,7 +60,7 @@ Mr Demo Portal</field>
<record id="message_company_news0_comment2" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="company_news_feed"/>
<field name="body"><![CDATA[<p>This feature is realy great! We will be able to communicate directly to our partners!</p>]]></field>
<field name="body"><![CDATA[<p>This feature is really great! We will be able to communicate directly to our partners!</p>]]></field>
<field name="parent_id" ref="message_company_news0"/>
<field name="type">comment</field>
<field name="subtype_id" ref="mail.mt_comment"/>

View File

@ -4,3 +4,5 @@ access_res_partner,res.partner,base.model_res_partner,portal.group_portal,1,0,0,
access_res_partner_address,res.partner_address,base.model_res_partner_address,portal.group_portal,1,0,0,0
access_res_partner_category,res.partner_category,base.model_res_partner_category,portal.group_portal,1,0,0,0
access_res_partner_title,res.partner_title,base.model_res_partner_title,portal.group_portal,1,0,0,0
access_acquirer,portal.payment.acquirer,portal.model_portal_payment_acquirer,,1,0,0,0
access_acquirer_all,portal.payment.acquirer,portal.model_portal_payment_acquirer,base.group_system,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
4 access_res_partner_address res.partner_address base.model_res_partner_address portal.group_portal 1 0 0 0
5 access_res_partner_category res.partner_category base.model_res_partner_category portal.group_portal 1 0 0 0
6 access_res_partner_title res.partner_title base.model_res_partner_title portal.group_portal 1 0 0 0
7 access_acquirer portal.payment.acquirer portal.model_portal_payment_acquirer 1 0 0 0
8 access_acquirer_all portal.payment.acquirer portal.model_portal_payment_acquirer base.group_system 1 1 1 1

View File

@ -0,0 +1,81 @@
.openerp .oe_application .oe_form_sheetbg {
/* Establish a stacking context on top of which the
payment_acquirers::before element can be positioned */
position: relative;
z-index: 0;
}
.openerp .payment_acquirers {
margin: -40px 0 -32px -24px;
position: relative;
padding: 10px 15px;
right: -153px;
background: #729FCF;
background-image: -webkit-gradient(linear, left top, left bottom, from(#729FCF), to(#3465A4));
background-image: -webkit-linear-gradient(top, #729FCF, #3465A4);
background-image: -moz-linear-gradient(top, #729FCF, #3465A4);
background-image: -ms-linear-gradient(top, #729FCF, #3465A4);
background-image: -o-linear-gradient(top, #729FCF, #3465A4);
background-image: linear-gradient(to bottom, #729FCF, #3465A4);
border-bottom: 1px solid #043574;
-webkit-box-shadow: 0 4px 20px rgba(0, 0, 0, 0.45);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.45);
}
.openerp .payment_acquirers form {
display: inline-block;
vertical-align: top;
}
.openerp .payment_acquirers form input,
.openerp .payment_acquirers form textarea,
.openerp .payment_acquirers form select
{
-webkit-box-shadow: none;
box-shadow: none;
background: transparent;
border: none;
padding: none;
}
.openerp .payment_acquirers::after {
content: " ";
display: block;
width: 10px;
height: 20px;
position: absolute;
bottom: 0;
right: 1px;
margin-bottom: -6px;
background: #043574;
-webkit-transform: skewY(-45deg);
-moz-transform: skewY(-45deg);
-ms-transform: skewY(-45deg);
-o-transform: skewY(-45deg);
transform: skewY(-45deg);
-webkit-box-shadow: inset 1px -1px 2px black, -1px 1px 3px black;
box-shadow: inset 1px -1px 2px black, -1px 1px 3px black;
/* push it under all its siblings, just on top of its root
in the z-index stack: div.oe_form_sheetbg */
z-index: -1;
}
.openerp .payment_acquirers .payment_header {
display: inline-block;
font-weight: bold;
font-size: 110%;
padding-right: 15px;
color: white;
text-shadow: 0 1px 1px #729FCF, 0 -1px 1px #3465A4;
}
.openerp .payment_acquirers .payment_header .payment_amount {
font-size: 130%;
padding: 6px 0px;
}

View File

@ -19,3 +19,5 @@
#
##############################################################################
import portal_sale
import res_config

View File

@ -26,18 +26,34 @@
'category': 'Tools',
'complexity': 'easy',
'description': """
This module adds sale menu and features to your portal if sale and portal are installed.
========================================================================================
This module adds a Sales menu to your portal as soon as sale and portal are installed.
======================================================================================
After installing this module, portal users will be able to access their own documents
via the following menus:
- Quotations
- Sale Orders
- Delivery Orders
- Products (public ones)
- Invoices
- Payments/Refunds
If online payment acquirers are configured, portal users will also be given the opportunity to
pay online on their Sale Orders and Invoices that are not paid yet. Paypal is included
by default, you simply need to configure a Paypal account in the Accounting/Invoicing settings.
""",
'author': 'OpenERP SA',
'depends': ['sale_stock','portal'],
'data': [
'security/portal_security.xml',
'portal_sale_view.xml',
'portal_sale_data.xml',
'res_config_view.xml',
'security/ir.model.access.csv',
],
'installable': True,
'auto_install': True,
'category': 'Hidden',
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 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
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import osv, fields
class sale_order(osv.Model):
_inherit = 'sale.order'
# make the real method inheritable
_payment_block_proxy = lambda self,*a,**kw: self._portal_payment_block(*a, **kw)
_columns = {
'portal_payment_options': fields.function(_payment_block_proxy, type="html", string="Portal Payment Options"),
}
def _portal_payment_block(self, cr, uid, ids, fieldname, arg, context=None):
result = dict.fromkeys(ids, False)
payment_acquirer = self.pool.get('portal.payment.acquirer')
for this in self.browse(cr, uid, ids, context=context):
if this.state not in ('draft','cancel') and not this.invoiced:
result[this.id] = payment_acquirer.render_payment_block(cr, uid, this, this.name,
this.pricelist_id.currency_id, this.amount_total, context=context)
return result
def action_quotation_send(self, cr, uid, ids, context=None):
''' Override to use a modified template that includes a portal signup link '''
action_dict = super(sale_order, self).action_quotation_send(cr, uid, ids, context=context)
try:
template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'portal_sale', 'email_template_edi_sale')[1]
# assume context is still a dict, as prepared by super
ctx = action_dict['context']
ctx['default_template_id'] = template_id
ctx['default_use_template'] = True
except Exception:
pass
return action_dict
class account_invoice(osv.Model):
_inherit = 'account.invoice'
# make the real method inheritable
_payment_block_proxy = lambda self,*a,**kw: self._portal_payment_block(*a, **kw)
_columns = {
'portal_payment_options': fields.function(_payment_block_proxy, type="html", string="Portal Payment Options"),
}
def _portal_payment_block(self, cr, uid, ids, fieldname, arg, context=None):
result = dict.fromkeys(ids, False)
payment_acquirer = self.pool.get('portal.payment.acquirer')
for this in self.browse(cr, uid, ids, context=context):
if this.state not in ('draft','done') and not this.reconciled:
result[this.id] = payment_acquirer.render_payment_block(cr, uid, this, this.number,
this.currency_id, this.residual, context=context)
return result
def action_invoice_sent(self, cr, uid, ids, context=None):
''' Override to use a modified template that includes a portal signup link '''
action_dict = super(account_invoice, self).action_invoice_sent(cr, uid, ids, context=context)
try:
template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'portal_sale', 'email_template_edi_invoice')[1]
# assume context is still a dict, as prepared by super
ctx = action_dict['context']
ctx['default_template_id'] = template_id
ctx['default_use_template'] = True
except Exception:
pass
return action_dict

View File

@ -0,0 +1,212 @@
<openerp>
<!-- 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">Sale Order - Send by Email (Portal)</field>
<field name="email_from">${object.user_id.email or ''}</field>
<field name="subject">${object.company_id.name} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })</field>
<field name="email_recipients">${object.partner_invoice_id.id}</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="auto_delete" eval="True"/>
<field name="report_template" ref="sale.report_sale_order"/>
<field name="report_name">${(object.name or '').replace('/','_')}_${object.state == 'draft' and 'draft' or ''}</field>
<field name="body_html"><![CDATA[
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); ">
<p>Hello${object.partner_id.name and ' ' or ''}${object.partner_id.name or ''},</p>
<p>Here is your ${object.state in ('draft', 'sent') and 'quotation' or 'order confirmation'} from ${object.company_id.name}: </p>
<p style="border-left: 1px solid #8e0000; margin-left: 30px;">
&nbsp;&nbsp;<strong>REFERENCES</strong><br />
&nbsp;&nbsp;Order number: <strong>${object.name}</strong><br />
&nbsp;&nbsp;Order total: <strong>${object.amount_total} ${object.pricelist_id.currency_id.name}</strong><br />
&nbsp;&nbsp;Order date: ${object.date_order}<br />
% if object.origin:
&nbsp;&nbsp;Order reference: ${object.origin}<br />
% endif
% if object.client_order_ref:
&nbsp;&nbsp;Your reference: ${object.client_order_ref}<br />
% endif
% if object.user_id:
&nbsp;&nbsp;Your contact: <a href="mailto:${object.user_id.email or ''}?subject=Order%20${object.name}">${object.user_id.name}</a>
% endif
</p>
<%
action = 'portal_sale.action_quotations_portal' if object.state in ('draft', 'sent') else 'portal_sale.action_orders_portal'
object.partner_id.signup_prepare()
signup_url = object.partner_id._get_signup_url_for_action(action=action,view_type='form',res_id=object.id)[object.partner_id.id]
%>
% if signup_url:
<p>
You can access this document and pay online via our Customer Portal:
</p>
<a style="display:block; width: 150px; height:20px; margin-left: 120px; color: #DDD; 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="${signup_url}">View ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'}</a>
% endif
% if object.order_policy in ('prepaid','manual') and object.company_id.paypal_account and object.state != 'draft':
<%
comp_name = quote(object.company_id.name)
order_name = quote(object.name)
paypal_account = quote(object.company_id.paypal_account)
order_amount = quote(str(object.residual))
cur_name = quote(object.pricelist_id.currency_id.name)
paypal_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&amp;business=%s&amp;item_name=%s%%20Order%%20%s" \
"&amp;invoice=%s&amp;amount=%s&amp;currency_code=%s&amp;button_subtype=services&amp;no_note=1" \
"&amp;bn=OpenERP_Order_PayNow_%s" % \
(paypal_account,comp_name,order_name,order_name,order_amount,cur_name,cur_name)
%>
<br/>
<p>It is also possible to directly pay with Paypal:</p>
<a style="margin-left: 120px;" href="${paypal_url}">
<img class="oe_edi_paypal_button" src="https://www.paypal.com/en_US/i/btn/btn_paynowCC_LG.gif"/>
</a>
% endif
<br/>
<p>If you have any question, do not hesitate to contact us.</p>
<p>Thank you for choosing ${object.company_id.name or 'us'}!</p>
<br/>
<br/>
<div style="width: 375px; margin: 0px; padding: 0px; background-color: #8E0000; border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; background-repeat: repeat no-repeat;">
<h3 style="margin: 0px; padding: 2px 14px; font-size: 12px; color: #FFF;">
<strong style="text-transform:uppercase;">${object.company_id.name}</strong></h3>
</div>
<div style="width: 347px; margin: 0px; padding: 5px 14px; line-height: 16px; background-color: #F2F2F2;">
<span style="color: #222; margin-bottom: 5px; display: block; ">
% if object.company_id.street:
${object.company_id.street}<br/>
% endif
% if object.company_id.street2:
${object.company_id.street2}<br/>
% endif
% if object.company_id.city or object.company_id.zip:
${object.company_id.zip} ${object.company_id.city}<br/>
% endif
% if object.company_id.country_id:
${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}<br/>
% endif
</span>
% if object.company_id.phone:
<div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">
Phone:&nbsp; ${object.company_id.phone}
</div>
% endif
% if object.company_id.website:
<div>
Web :&nbsp;<a href="${object.company_id.website}">${object.company_id.website}</a>
</div>
%endif
<p></p>
</div>
</div>
]]></field>
</record>
<record id="email_template_edi_invoice" model="email.template">
<field name="name">Invoice - Send by Email (Portal)</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_recipients">${object.partner_id.id}</field>
<field name="model_id" ref="account.model_account_invoice"/>
<field name="auto_delete" eval="True"/>
<field name="report_template" ref="account.account_invoices"/>
<field name="report_name">Invoice_${(object.number or '').replace('/','_')}_${object.state == 'draft' and 'draft' or ''}</field>
<field name="body_html"><![CDATA[
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); ">
<p>Hello${object.partner_id.name and ' ' or ''}${object.partner_id.name or ''},</p>
<p>A new invoice is available for you: </p>
<p style="border-left: 1px solid #8e0000; margin-left: 30px;">
&nbsp;&nbsp;<strong>REFERENCES</strong><br />
&nbsp;&nbsp;Invoice number: <strong>${object.number}</strong><br />
&nbsp;&nbsp;Invoice total: <strong>${object.amount_total} ${object.currency_id.name}</strong><br />
&nbsp;&nbsp;Invoice date: ${object.date_invoice}<br />
% if object.origin:
&nbsp;&nbsp;Order reference: ${object.origin}<br />
% endif
% if object.user_id:
&nbsp;&nbsp;Your contact: <a href="mailto:${object.user_id.email or ''}?subject=Invoice%20${object.number}">${object.user_id.name}</a>
% endif
</p>
<%
action = 'portal_sale.portal_action_invoices'
object.partner_id.signup_prepare()
signup_url = object.partner_id._get_signup_url_for_action(action=action,view_type='form',res_id=object.id)[object.partner_id.id]
%>
% if signup_url:
<p>
You can access the invoice document and pay online via our Customer Portal:
</p>
<a style="display:block; width: 150px; height:20px; margin-left: 120px; color: #DDD; 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="${signup_url}">View Invoice</a>
% endif
% if object.company_id.paypal_account and object.type in ('out_invoice', 'in_refund'):
<%
comp_name = quote(object.company_id.name)
inv_number = quote(object.number)
paypal_account = quote(object.company_id.paypal_account)
inv_amount = quote(str(object.residual))
cur_name = quote(object.currency_id.name)
paypal_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&amp;business=%s&amp;item_name=%s%%20Invoice%%20%s&amp;" \
"invoice=%s&amp;amount=%s&amp;currency_code=%s&amp;button_subtype=services&amp;no_note=1&amp;bn=OpenERP_Invoice_PayNow_%s" % \
(paypal_account,comp_name,inv_number,inv_number,inv_amount,cur_name,cur_name)
%>
<br/>
<p>It is also possible to directly pay with Paypal:</p>
<a style="margin-left: 120px;" href="${paypal_url}">
<img class="oe_edi_paypal_button" src="https://www.paypal.com/en_US/i/btn/btn_paynowCC_LG.gif"/>
</a>
% endif
<br/>
<p>If you have any question, do not hesitate to contact us.</p>
<p>Thank you for choosing ${object.company_id.name or 'us'}!</p>
<br/>
<br/>
<div style="width: 375px; margin: 0px; padding: 0px; background-color: #8E0000; border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; background-repeat: repeat no-repeat;">
<h3 style="margin: 0px; padding: 2px 14px; font-size: 12px; color: #FFF;">
<strong style="text-transform:uppercase;">${object.company_id.name}</strong></h3>
</div>
<div style="width: 347px; margin: 0px; padding: 5px 14px; line-height: 16px; background-color: #F2F2F2;">
<span style="color: #222; margin-bottom: 5px; display: block; ">
% if object.company_id.street:
${object.company_id.street}<br/>
% endif
% if object.company_id.street2:
${object.company_id.street2}<br/>
% endif
% if object.company_id.city or object.company_id.zip:
${object.company_id.zip} ${object.company_id.city}<br/>
% endif
% if object.company_id.country_id:
${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}<br/>
% endif
</span>
% if object.company_id.phone:
<div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">
Phone:&nbsp; ${object.company_id.phone}
</div>
% endif
% if object.company_id.website:
<div>
Web :&nbsp;<a href="${object.company_id.website}">${object.company_id.website}</a>
</div>
%endif
<p></p>
</div>
</div>
]]></field>
</record>
</data>
</openerp>

View File

@ -1,12 +1,35 @@
<?xml version="1.0"?>
<openerp>
<data>
<!-- Add payment options to sale.order and invoice forms -->
<record model="ir.ui.view" id="sale_order_form_payment">
<field name="name">sale.order.form.payment</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<notebook version="7.0" position="before">
<field name="portal_payment_options" groups="portal_sale.group_payment_options"/>
</notebook>
</field>
</record>
<record model="ir.ui.view" id="invoice_form_payment">
<field name="name">account.invoice.form.payment</field>
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_form"/>
<field name="arch" type="xml">
<notebook version="7.0" position="before">
<field name="portal_payment_options" groups="portal_sale.group_payment_options"/>
</notebook>
</field>
</record>
<!--
Override the original action to set another help field and/or
another context field, more suited for portal members
-->
<record id="action_order_tree5" model="ir.actions.act_window">
<record id="action_quotations_portal" model="ir.actions.act_window">
<field name="name">Quotations</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.order</field>
@ -16,8 +39,8 @@
<field name="help">You don't have any quotation.</field>
</record>
<record id="action_order_form" model="ir.actions.act_window">
<field name="name">Sales Orders</field>
<record id="action_orders_portal" model="ir.actions.act_window">
<field name="name">Sale Orders</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.order</field>
<field name="view_mode">tree,form,calendar,graph</field>
@ -48,8 +71,8 @@
<field name="help">There are no public products.</field>
</record>
<record id="action_invoice_tree1" model="ir.actions.act_window">
<field name="name">Customer Invoices</field>
<record id="portal_action_invoices" model="ir.actions.act_window">
<field name="name">Invoices</field>
<field name="res_model">account.invoice</field>
<field name="view_mode">tree,form,calendar,graph</field>
<field name="domain">[('type','=','out_invoice')]</field>
@ -57,9 +80,21 @@
<field name="search_view_id" ref="account.view_account_invoice_filter"/>
<field name="help">You don't have any invoice.</field>
</record>
<record id="portal_action_invoices_tree_spec" model="ir.actions.act_window.view">
<field name="act_window_id" ref="portal_action_invoices"/>
<field name="view_id" ref="account.invoice_tree"/>
<field name="view_mode">tree</field>
<field name="sequence" eval="0"/>
</record>
<record id="portal_action_invoices_form_spec" model="ir.actions.act_window.view">
<field name="act_window_id" ref="portal_action_invoices"/>
<field name="view_id" ref="account.invoice_form"/>
<field name="view_mode">form</field>
<field name="sequence" eval="1"/>
</record>
<record id="action_vendor_receipt" model="ir.actions.act_window">
<field name="name">Customer Payment</field>
<record id="portal_action_vouchers" model="ir.actions.act_window">
<field name="name">Refunds/Payments</field>
<field name="res_model">account.voucher</field>
<field name="domain">[('journal_id.type', 'in', ['bank', 'cash']), ('type','=','receipt')]</field>
<field name="context">{'type':'receipt'}</field>
@ -68,19 +103,17 @@
<field name="help">You don't have any refunds or payments.</field>
</record>
<menuitem name="Quotations" id="portal_quotations" parent="portal.portal_orders"
action="action_order_tree5" sequence="10"/>
<menuitem name="Sales Orders" id="portal_sales_orders" parent="portal.portal_orders"
action="action_order_form" sequence="20"/>
<menuitem name="Delivery Orders" id="portal_delivery" parent="portal.portal_orders"
<menuitem id="portal_quotations" parent="portal.portal_orders"
action="action_quotations_portal" sequence="10"/>
<menuitem id="portal_sales_orders" parent="portal.portal_orders"
action="action_orders_portal" sequence="20"/>
<menuitem id="portal_delivery" parent="portal.portal_orders"
action="action_picking_tree" sequence="30"/>
<menuitem name="Products" id="portal_products" parent="portal.portal_orders"
<menuitem id="portal_products" parent="portal.portal_orders"
action="product_normal_action" sequence="40"/>
<menuitem name="Invoice" id="portal_invoices" parent="portal.portal_invoices_payements"
action="action_invoice_tree1" sequence="10"/>
<menuitem name="Refund/Payments" id="portal_payments" parent="portal.portal_invoices_payements"
action="action_vendor_receipt" sequence="20"/>
<menuitem id="portal_invoices" parent="portal.portal_invoices_payements"
action="portal_action_invoices" sequence="10"/>
<menuitem id="portal_payments" parent="portal.portal_invoices_payements"
action="portal_action_vouchers" sequence="20"/>
</data>
</openerp>

View File

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2012-TODAY 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
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import fields, osv
class sale_portal_config_settings(osv.TransientModel):
_inherit = 'account.config.settings'
_columns = {
'group_payment_options': fields.boolean('Show payment buttons to employees too',
implied_group='portal_sale.group_payment_options',
help="Show online payment options on Sale Orders and Customer Invoices to employees. "
"If not checked, these options are only visible to portal users."),
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0"?>
<openerp>
<data>
<!-- Add payment options to sale.order and invoice forms -->
<record model="ir.ui.view" id="portal_sale_payment_option_config">
<field name="model">account.config.settings</field>
<field name="inherit_id" ref="account.view_account_config_settings"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='bank_cash']/div" version="7.0" position="inside">
<div>
<field name="group_payment_options" class="oe_inline"/>
<label for="group_payment_options"/>
<button name='%(portal.action_acquirer_list)d' type="action"
string="Configure payment acquiring methods" class="oe_link"/>
</div>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@ -2,6 +2,18 @@
<openerp>
<data noupdate="1">
<record id="group_payment_options" model="res.groups">
<field name="name">View Online Payment Options</field>
<field name="category_id" ref="base.module_category_hidden"/>
<field name="comment">Members of this group see the online payment options
on Sale Orders and Customer Invoices. These options are meant for customers who are accessing
their documents through the portal.</field>
</record>
<!-- Make the payment_options group implied for all portal users -->
<record id="portal.group_portal" model="res.groups">
<field name="implied_ids" eval="[(4,ref('group_payment_options'))]"/>
</record>
<!-- Sale Portal Access Rules -->
<record id="portal_sale_order_user_rule" model="ir.rule">
<field name="name">Portal Personal Quotations/Sales Orders</field>

View File

@ -100,6 +100,23 @@ class product_uom(osv.osv):
def _factor_inv_write(self, cursor, user, id, name, value, arg, context=None):
return self.write(cursor, user, id, {'factor': self._compute_factor_inv(value)}, context=context)
def name_create(self, cr, uid, name, context=None):
""" The UoM category and factor are required, so we'll have to add temporary values
for imported UoMs """
uom_categ = self.pool.get('product.uom.categ')
# look for the category based on the english name, i.e. no context on purpose!
# TODO: should find a way to have it translated but not created until actually used
categ_misc = 'Unsorted/Imported Units'
categ_id = uom_categ.search(cr, uid, [('name', '=', categ_misc)])
if categ_id:
categ_id = categ_id[0]
else:
categ_id, _ = uom_categ.name_create(cr, uid, categ_misc)
uom_id = self.create(cr, uid, {self._rec_name: name,
'category_id': categ_id,
'factor': 1})
return self.name_get(cr, uid, [uom_id], context=context)[0]
def create(self, cr, uid, data, context=None):
if 'factor_inv' in data:
if data['factor_inv'] <> 1:

View File

@ -1247,7 +1247,7 @@ class task(base_stage, osv.osv):
}
for line in msg['body'].split('\n'):
line = line.strip()
res = tools.misc.command_re.match(line)
res = tools.command_re.match(line)
if res:
match = res.group(1).lower()
field = maps.get(match)

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)
@ -106,22 +91,26 @@ class purchase_order(osv.osv, EDIMixin):
# the desired company among the user's allowed companies
self._edi_requires_attributes(('company_id','company_address'), edi_document)
res_partner_obj = self.pool.get('res.partner')
res_partner = self.pool.get('res.partner')
# imported company_address = new partner address
src_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:
address_info['name'] = src_company_name
address_id = res_partner_obj.edi_import(cr, uid, address_info, context=context)
xid, company_name = edi_document.pop('company_id')
# Retrofit address info into a unified partner info (changed in v7 - used to keep them separate)
company_address_edi = edi_document.pop('company_address')
company_address_edi['name'] = company_name
company_address_edi['is_company'] = True
company_address_edi['__import_model'] = 'res.partner'
company_address_edi['__id'] = xid # override address ID, as of v7 they should be the same anyway
if company_address_edi.get('logo'):
company_address_edi['image'] = company_address_edi.pop('logo')
company_address_edi['supplier'] = True
partner_id = res_partner.edi_import(cr, uid, company_address_edi, context=context)
# modify edi_document to refer to new partner/address
partner_address = res_partner_obj.browse(cr, uid, address_id, context=context)
edi_document.pop('partner_address', False) # ignored
edi_document['partner_id'] = self.edi_m2o(cr, uid, partner_address, context=context)
return address_id
# modify edi_document to refer to new partner
partner = res_partner.browse(cr, uid, partner_id, context=context)
partner_edi_m2o = self.edi_m2o(cr, uid, partner, context=context)
edi_document['partner_id'] = partner_edi_m2o
edi_document.pop('partner_address', None) # ignored, that's supposed to be our own address!
return partner_id
def _edi_get_pricelist(self, cr, uid, partner_id, currency, context=None):
# TODO: refactor into common place for purchase/sale, e.g. into product module

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,24 +13,19 @@
</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_recipients">${object.partner_id.id}</field>
<field name="model_id" ref="purchase.model_purchase_order"/>
<field name="auto_delete" eval="True"/>
<field name="report_template" ref="report_purchase_quotation"/>
<field name="report_name">RFQ_${(object.name or '').replace('/','_')}</field>
<field name="body_html"><![CDATA[
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); ">
@ -61,15 +44,11 @@
% if object.partner_ref:
&nbsp;&nbsp;Your reference: ${object.partner_ref}<br />
% endif
% if object.user_id:
&nbsp;&nbsp;Your contact: <a href="mailto:${object.validator.email or ''}?subject=Order%20${object.name}">${object.validator.name}</a>
% endif
</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

@ -384,29 +384,32 @@ class purchase_order(osv.osv):
'''
This function opens a window to compose an email, with the edi purchase template message loaded by default
'''
mod_obj = self.pool.get('ir.model.data')
template = mod_obj.get_object_reference(cr, uid, 'purchase', 'email_template_edi_purchase')
template_id = template and template[1] or False
res = mod_obj.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')
res_id = res and res[1] or False
ir_model_data = self.pool.get('ir.model.data')
try:
template_id = ir_model_data.get_object_reference(cr, uid, 'purchase', 'email_template_edi_purchase')[1]
except ValueError:
template_id = False
try:
compose_form_id = ir_model_data.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')[1]
except ValueError:
compose_form_id = False
ctx = dict(context)
ctx.update({
'default_model': 'purchase.order',
'default_res_id': ids[0],
'default_use_template': True,
'default_use_template': bool(template_id),
'default_template_id': template_id,
'default_composition_mode': 'comment',
})
})
return {
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
'res_model': 'mail.compose.message',
'views': [(res_id, 'form')],
'view_id': res_id,
'type': 'ir.actions.act_window',
'views': [(compose_form_id, 'form')],
'view_id': compose_form_id,
'target': 'new',
'context': ctx,
'nodestroy': True,
}
#TODO: implement messages system

View File

@ -27,36 +27,36 @@
-
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
Then I import a sample EDI document of a sale order (v7.0)
-
!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",
"__id": "sale:724f9v70-dv70-1v70-8v70-701a04e25v70.sale_order_test",
"__module": "sale",
"__model": "sale.order",
"__import_module": "purchase",
"__import_model": "purchase.order",
"__version": [6,1,0],
"__version": [7,0,0],
"name": "SO008",
"currency": {
"__id": "base:724f93ec-ddd0-11e0-88ec-701a04e25543.EUR",
"__id": "base:724f9v70-dv70-1v70-8v70-701a04e25v70.EUR",
"__module": "base",
"__model": "res.currency",
"code": "EUR",
"symbol": "€",
},
"date_order": "2011-09-13",
"partner_id": ["sale:724f93ec-ddd0-11e0-88ec-701a04e25543.res_partner_test22", "Junjun wala"],
"partner_id": ["sale:724f9v70-dv70-1v70-8v70-701a04e25v70.res_partner_test22", "Junjun wala"],
"partner_address": {
"__id": "base:724f93ec-ddd0-11e0-88ec-701a04e25543.res_partner_address_7wdsjasdjh",
"__id": "base:724f9v70-dv70-1v70-8v70-701a04e25v70.res_partner_address_7wdsjasdjh",
"__module": "base",
"__model": "res.partner",
"phone": "(+32).81.81.37.00",
@ -65,48 +65,48 @@
"zip": "1367",
"country_id": ["base:5af1272e-dd26-11e0-b65e-701a04e25543.be", "Belgium"],
},
"company_id": ["base:724f93ec-ddd0-11e0-88ec-701a04e25543.main_company", "Supplier S.A."],
"company_id": ["base:724f9v70-dv70-1v70-8v70-701a04e25v70.main_company", "Supplier S.A."],
"company_address": {
"__id": "base:724f93ec-ddd0-11e0-88ec-701a04e25543.main_address",
"__id": "base:724f9v70-dv70-1v70-8v70-701a04e25v70.main_address",
"__module": "base",
"__model": "res.partner",
"city": "Gerompont",
"zip": "1367",
"country_id": ["base:724f93ec-ddd0-11e0-88ec-701a04e25543.be", "Belgium"],
"country_id": ["base:724f9v70-dv70-1v70-8v70-701a04e25v70.be", "Belgium"],
"phone": "(+32).81.81.37.00",
"street": "Chaussee de Namur 40",
"street2": "mailbox 34",
"bank_ids": [
["base:724f93ec-ddd0-11e0-88ec-701a04e25543.res_partner_bank-XiwqnxKWzGbp","Guys bank: 123477777-156113"]
["base:724f9v70-dv70-1v70-8v70-701a04e25v70.res_partner_bank-XiwqnxKWzGbp","Guys bank: 123477777-156113"]
],
},
"order_line": [{
"__id": "sale:724f93ec-ddd0-11e0-88ec-701a04e25543.sale_order_line-LXEqeuI-SSP0",
"__id": "sale:724f9v70-dv70-1v70-8v70-701a04e25v70.sale_order_line-LXEqeuI-SSP0",
"__module": "sale",
"__model": "sale.order.line",
"__import_module": "purchase",
"__import_model": "purchase.order.line",
"name": "PC Assemble SC234",
"product_uom": ["product:724f93ec-ddd0-11e0-88ec-701a04e25543.product_uom_unit", "Unit"],
"product_uom": ["product:724f9v70-dv70-1v70-8v70-701a04e25v70.product_uom_unit", "Unit"],
"product_qty": 1.0,
"date_planned": "2011-09-30",
"sequence": 10,
"price_unit": 150.0,
"product_id": ["product:724f93ec-ddd0-11e0-88ec-701a04e25543.product_product_3", "[PCSC234] PC Assemble SC234"],
"product_id": ["product:724f9v70-dv70-1v70-8v70-701a04e25v70.product_product_3", "[PCSC234] PC Assemble SC234"],
},
{
"__id": "sale:724f93ec-ddd0-11e0-88ec-701a04e25543.sale_order_line-LXEqeadasdad",
"__id": "sale:724f9v70-dv70-1v70-8v70-701a04e25v70.sale_order_line-LXEqeadasdad",
"__module": "sale",
"__model": "sale.order.line",
"__import_module": "purchase",
"__import_model": "purchase.order.line",
"name": "PC on Demand",
"product_uom": ["product:724f93ec-ddd0-11e0-88ec-701a04e25543.product_uom_unit", "Unit"],
"product_uom": ["product:724f9v70-dv70-1v70-8v70-701a04e25v70.product_uom_unit", "Unit"],
"product_qty": 10.0,
"sequence": 11,
"date_planned": "2011-09-15",
"price_unit": 20.0,
"product_id": ["product:724f93ec-ddd0-11e0-88ec-701a04e25543.product_product_5", "[PC-DEM] PC on Demand"],
"product_id": ["product:724f9v70-dv70-1v70-8v70-701a04e25v70.product_product_5", "[PC-DEM] PC on Demand"],
}],
}
new_purchase_order_id = purchase_order_pool.edi_import(cr, uid, edi_document, context=context)
@ -114,6 +114,7 @@
order_new = purchase_order_pool.browse(cr, uid, new_purchase_order_id)
# check bank info on partner
assert order_new.partner_id.supplier, "Imported partner should be a supplier, as we just imported the document as a purchase order"
assert len(order_new.partner_id.bank_ids) == 1, "Expected 1 bank entry related to partner"
bank_info = order_new.partner_id.bank_ids[0]
assert bank_info.acc_number == "Guys bank: 123477777-156113", 'Expected "Guys bank: 123477777-156113", got %s' % bank_info.acc_number
@ -133,3 +134,104 @@
assert purchase_line.product_qty == 10 , "product qty is not same"
else:
raise AssertionError('unknown order line: %s' % purchase_line)
-
"Then I import a sample EDI document of a sale order (v6.1 - to test backwards compatibility)"
-
!python {model: edi.edi}: |
purchase_order_pool = self.pool.get('purchase.order')
edi_document = {
"__id": "sale:724f93ec-ddd0-11e0-88ec-701a04e25543.sale_order_test",
"__module": "sale",
"__model": "sale.order",
"__import_module": "purchase",
"__import_model": "purchase.order",
"__version": [6,1,0],
"name": "SO08v61",
"currency": {
"__id": "base:724f93ec-ddd0-11e0-88ec-701a04e25543.EUR",
"__module": "base",
"__model": "res.currency",
"code": "EUR",
"symbol": "€",
},
"date_order": "2011-09-13",
"partner_id": ["sale:724f93ec-ddd0-11e0-88ec-701a04e25543.res_partner_test22", "Junjun wala"],
"partner_address": {
"__id": "base:724f93ec-ddd0-11e0-88ec-701a04e25543.res_partner_address_7wdsjasdjh",
"__module": "base",
"__model": "res.partner.address",
"phone": "(+32).81.81.37.00",
"street": "Chaussee de Namur 40",
"city": "Gerompont",
"zip": "1367",
"country_id": ["base:5af1272e-dd26-11e0-b65e-701a04e25543.be", "Belgium"],
},
"company_id": ["base:724f93ec-ddd0-11e0-88ec-701a04e25543.main_company", "Supplier S.A."],
"company_address": {
"__id": "base:724f93ec-ddd0-11e0-88ec-701a04e25543.main_address",
"__module": "base",
"__model": "res.partner.address",
"city": "Gerompont",
"zip": "1367",
"country_id": ["base:724f93ec-ddd0-11e0-88ec-701a04e25543.be", "Belgium"],
"phone": "(+32).81.81.37.00",
"street": "Chaussee de Namur 40",
"street2": "mailbox 34",
"bank_ids": [
["base:724f93ec-ddd0-11e0-88ec-701a04e25543.res_partner_bank-XiwqnxKWzGbp","Another bank: 123477700-156113"]
],
},
"order_line": [{
"__id": "sale:724f93ec-ddd0-11e0-88ec-701a04e25543.sale_order_line-LXEqeuI-SSP0",
"__module": "sale",
"__model": "sale.order.line",
"__import_module": "purchase",
"__import_model": "purchase.order.line",
"name": "Basic PC",
"product_uom": ["product:724f93ec-ddd0-11e0-88ec-701a04e25543.product_uom_unit", "PCE"],
"product_qty": 1.0,
"date_planned": "2011-09-30",
"sequence": 10,
"price_unit": 150.0,
"product_id": ["product:724f93ec-ddd0-11e0-88ec-701a04e25543.product_product_pc1", "[PC1] Basic PC"],
},
{
"__id": "sale:724f93ec-ddd0-11e0-88ec-701a04e25543.sale_order_line-LXEqeadasdad",
"__module": "sale",
"__model": "sale.order.line",
"__import_module": "purchase",
"__import_model": "purchase.order.line",
"name": "Medium PC",
"product_uom": ["product:724f93ec-ddd0-11e0-88ec-701a04e25543.product_uom_unit", "PCE"],
"product_qty": 10.0,
"sequence": 11,
"date_planned": "2011-09-15",
"price_unit": 20.0,
"product_id": ["product:724f93ec-ddd0-11e0-88ec-701a04e25543.product_product_pc3", "[PC3] Medium PC"],
}],
}
new_purchase_order_id = purchase_order_pool.edi_import(cr, uid, edi_document, context=context)
assert new_purchase_order_id, 'Purchase order import failed'
order_new = purchase_order_pool.browse(cr, uid, new_purchase_order_id)
# check bank info on partner
assert order_new.partner_id.supplier, "Imported partner should be a supplier, as we just imported the document as a purchase order"
assert len(order_new.partner_id.bank_ids) == 1, "Expected 1 bank entry related to partner"
bank_info = order_new.partner_id.bank_ids[0]
assert bank_info.acc_number == "Another bank: 123477700-156113", 'Expected "Another bank: 123477700-156113", got %s' % bank_info.acc_number
assert order_new.pricelist_id.name == 'Default Purchase Pricelist' , "Default Purchase Pricelist was not automatically assigned"
assert order_new.amount_total == 350, "Amount total is not same"
assert order_new.amount_untaxed == 350, "untaxed amount is not same"
assert len(order_new.order_line) == 2, "Purchase order lines number mismatch"
for purchase_line in order_new.order_line:
if purchase_line.name == 'Basic PC':
assert purchase_line.product_uom.name == "PCE" , "uom is not same"
assert purchase_line.price_unit == 150 , "unit price is not same, got %s, expected 150"%(purchase_line.price_unit,)
assert purchase_line.product_qty == 1 , "product qty is not same"
elif purchase_line.name == 'Medium PC':
assert purchase_line.product_uom.name == "PCE" , "uom is not same"
assert purchase_line.price_unit == 20 , "unit price is not same, got %s, expected 20"%(purchase_line.price_unit,)
assert purchase_line.product_qty == 10 , "product qty is not same"
else:
raise AssertionError('unknown order line: %s' % purchase_line)

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)
@ -102,34 +88,36 @@ class sale_order(osv.osv, EDIMixin):
edi_doc_list.append(edi_doc)
return edi_doc_list
def _edi_import_company(self, cr, uid, edi_document, context=None):
# TODO: for multi-company setups, we currently import the document in the
# user's current company, but we should perhaps foresee a way to select
# the desired company among the user's allowed companies
self._edi_requires_attributes(('company_id','company_address'), edi_document)
res_partner_obj = self.pool.get('res.partner')
res_partner = self.pool.get('res.partner')
# imported company_address = new partner address
src_company_id, src_company_name = edi_document.pop('company_id')
xid, company_name = edi_document.pop('company_id')
# Retrofit address info into a unified partner info (changed in v7 - used to keep them separate)
company_address_edi = edi_document.pop('company_address')
company_address_edi['name'] = company_name
company_address_edi['is_company'] = True
company_address_edi['__import_model'] = 'res.partner'
company_address_edi['__id'] = xid # override address ID, as of v7 they should be the same anyway
if company_address_edi.get('logo'):
company_address_edi['image'] = company_address_edi.pop('logo')
company_address_edi['customer'] = True
partner_id = res_partner.edi_import(cr, uid, company_address_edi, context=context)
address_info = edi_document.pop('company_address')
address_info['supplier'] = True
if 'name' not in address_info:
address_info['name'] = src_company_name
# modify edi_document to refer to new partner
partner = res_partner.browse(cr, uid, partner_id, context=context)
partner_edi_m2o = self.edi_m2o(cr, uid, partner, context=context)
edi_document['partner_id'] = partner_edi_m2o
edi_document['partner_invoice_id'] = partner_edi_m2o
edi_document['partner_shipping_id'] = partner_edi_m2o
address_id = res_partner_obj.edi_import(cr, uid, address_info, context=context)
edi_document.pop('partner_address', None) # ignored, that's supposed to be our own address!
return partner_id
# modify edi_document to refer to new partner/address
partner_address = res_partner_obj.browse(cr, uid, address_id, context=context)
edi_document.pop('partner_address', False) # ignored
address_edi_m2o = self.edi_m2o(cr, uid, partner_address, context=context)
edi_document['partner_id'] = address_edi_m2o
edi_document['partner_invoice_id'] = address_edi_m2o
edi_document['partner_shipping_id'] = address_edi_m2o
return address_id
def _edi_get_pricelist(self, cr, uid, partner_id, currency, context=None):
# TODO: refactor into common place for purchase/sale, e.g. into product module
@ -171,7 +159,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 +172,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,19 +14,20 @@
<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 Email</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="subject">${object.company_id.name} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })</field>
<field name="email_recipients">${object.partner_invoice_id.id}</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="auto_delete" eval="True"/>
<field name="report_template" ref="report_sale_order"/>
<field name="report_name">${(object.name or '').replace('/','_')}_${object.state == 'draft' and 'draft' or ''}</field>
<field name="body_html"><![CDATA[
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); ">
@ -46,16 +46,12 @@
% if object.client_order_ref:
&nbsp;&nbsp;Your reference: ${object.client_order_ref}<br />
% endif
% if object.user_id:
&nbsp;&nbsp;Your contact: <a href="mailto:${object.user_id.email or ''}?subject=Order%20${object.name}">${object.user_id.name}</a>
% endif
</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'):
% if object.order_policy in ('prepaid','manual') and object.company_id.paypal_account and object.state != 'draft':
<%
comp_name = quote(object.company_id.name)
order_name = quote(object.name)

View File

@ -636,30 +636,33 @@ class sale_order(osv.osv):
This function opens a window to compose an email, with the edi sale template message loaded by default
'''
assert len(ids) == 1, 'This option should only be used for a single id at a time.'
mod_obj = self.pool.get('ir.model.data')
template = mod_obj.get_object_reference(cr, uid, 'sale', 'email_template_edi_sale')
template_id = template and template[1] or False
res = mod_obj.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')
res_id = res and res[1] or False
ir_model_data = self.pool.get('ir.model.data')
try:
template_id = ir_model_data.get_object_reference(cr, uid, 'sale', 'email_template_edi_sale')[1]
except ValueError:
template_id = False
try:
compose_form_id = ir_model_data.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')[1]
except ValueError:
compose_form_id = False
ctx = dict(context)
ctx.update({
'default_model': 'sale.order',
'default_res_id': ids[0],
'default_use_template': True,
'default_use_template': bool(template_id),
'default_template_id': template_id,
'default_composition_mode': 'comment',
'mark_so_as_sent': True
})
return {
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
'res_model': 'mail.compose.message',
'views': [(res_id, 'form')],
'view_id': res_id,
'type': 'ir.actions.act_window',
'views': [(compose_form_id, 'form')],
'view_id': compose_form_id,
'target': 'new',
'context': ctx,
'nodestroy': True,
}
def action_done(self, cr, uid, ids, context=None):
@ -1025,17 +1028,14 @@ class sale_order_line(osv.osv):
raise osv.except_osv(_('Invalid Action!'), _('Cannot delete a sales order line which is in state \'%s\'.') %(rec.state,))
return super(sale_order_line, self).unlink(cr, uid, ids, context=context)
sale_order_line()
class mail_compose_message(osv.osv):
_inherit = 'mail.compose.message'
def send_mail(self, cr, uid, ids, context=None):
context = context or {}
if context.get('mark_so_as_sent', False) and context.get('default_res_id', False):
if context.get('default_model') == 'sale.order' and context.get('default_res_id') and context.get('mark_so_as_sent'):
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'sale.order', context.get('default_res_id', False), 'quotation_sent', cr)
wf_service.trg_validate(uid, 'sale.order', context['default_res_id'], 'quotation_sent', cr)
return super(mail_compose_message, self).send_mail(cr, uid, ids, context=context)
mail_compose_message()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -166,8 +166,8 @@
<header>
<button name="invoice_recreate" states="invoice_except" string="Recreate Invoice" groups="base.group_user"/>
<button name="invoice_corrected" states="invoice_except" string="Ignore Exception" groups="base.group_user"/>
<button name="action_quotation_send" string="Send by Mail" type="object" states="draft" class="oe_highlight" groups="base.group_user"/>
<button name="action_quotation_send" string="Send by Mail" type="object" states="sent" groups="base.group_user"/>
<button name="action_quotation_send" string="Send by Email" type="object" states="draft" class="oe_highlight" groups="base.group_user"/>
<button name="action_quotation_send" string="Send by Email" type="object" states="sent" groups="base.group_user"/>
<button name="print_quotation" string="Print" type="object" states="draft" class="oe_highlight" groups="base.group_user"/>
<button name="print_quotation" string="Print" type="object" states="sent" groups="base.group_user"/>
<button name="action_button_confirm" states="draft" string="Confirm Sale" type="object" groups="base.group_user"/>
@ -358,7 +358,7 @@
</field>
</record>
<record id="action_order_form" model="ir.actions.act_window">
<record id="action_orders" model="ir.actions.act_window">
<field name="name">Sale Orders</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.order</field>
@ -377,9 +377,9 @@
</p>
</field>
</record>
<menuitem action="action_order_form" id="menu_sale_order" parent="base.menu_sales" sequence="5" groups="base.group_sale_salesman,base.group_sale_manager"/>
<menuitem action="action_orders" id="menu_sale_order" parent="base.menu_sales" sequence="5" groups="base.group_sale_salesman,base.group_sale_manager"/>
<record id="action_order_tree2" model="ir.actions.act_window">
<record id="action_orders_exception" model="ir.actions.act_window">
<field name="name">Sales in Exception</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.order</field>
@ -390,7 +390,7 @@
<field name="search_view_id" ref="view_sales_order_filter"/>
</record>
<record id="action_order_tree4" model="ir.actions.act_window">
<record id="action_orders_in_progress" model="ir.actions.act_window">
<field name="name">Sales Order in Progress</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.order</field>
@ -401,7 +401,7 @@
</record>
<record id="action_order_tree5" model="ir.actions.act_window">
<record id="action_quotations" model="ir.actions.act_window">
<field name="name">Quotations</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.order</field>
@ -427,7 +427,7 @@
</record>
<menuitem id="menu_sale_quotations"
action="action_order_tree5" parent="base.menu_sales"
action="action_quotations" parent="base.menu_sales"
sequence="4"/>
<record id="action_order_tree" model="ir.actions.act_window">

View File

@ -25,70 +25,71 @@
-
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
"Then I import a sample EDI document of a purchase order (v7.0)"
-
!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",
"__id": "purchase:5af12v70-dv70-1v70-bv70-701a04e25v70.purchase_order_test",
"__module": "purchase",
"__model": "purchase.order",
"__import_module": "sale",
"__import_model": "sale.order",
"__version": [6,1,0],
"__version": [7,0,0],
"name": "PO00011",
"date_order": "2011-09-12",
"currency": {
"__id": "base:5af1272e-dd26-11e0-b65e-701a04e25543.EUR",
"__id": "base:5af12v70-dv70-1v70-bv70-701a04e25v70.EUR",
"__module": "base",
"__model": "res.currency",
"code": "EUR",
"symbol": "€",
},
"company_id": ["base:5af1272e-dd26-11e0-b65e-701a04e25543.main_company", "Client S.A."],
"company_id": ["base:5af12v70-dv70-1v70-bv70-701a04e25v70.main_company", "Client S.A."],
"company_address": {
"__id": "base:5af1272e-dd26-11e0-b65e-701a04e25543.some_address",
"__id": "base:5af12v70-dv70-1v70-bv70-701a04e25v70.some_address",
"__module": "base",
"__model": "res.partner",
"phone": "(+32).81.81.37.00",
"street": "Chaussee de Namur 40",
"city": "Gerompont",
"zip": "1367",
"country_id": ["base:5af1272e-dd26-11e0-b65e-701a04e25543.be", "Belgium"],
"country_id": ["base:5af12v70-dv70-1v70-bv70-701a04e25v70.be", "Belgium"],
"bank_ids": [
["base:5af1272e-dd26-11e0-b65e-701a04e25543.res_partner_bank-adaWadsadasdDJzGbp","Ladies bank: 032465789-156113"]
["base:5af12v70-dv70-1v70-bv70-701a04e25v70.res_partner_bank-adaWadsadasdDJzGbp","Another bank: 032465700-156700"]
],
},
"partner_id": ["purchase:5af1272e-dd26-11e0-b65e-701a04e25543.res_partner_test20", "jones white"],
"partner_id": ["purchase:5af12v70-dv70-1v70-bv70-701a04e25v70.res_partner_test20", "jones white"],
"order_line": [{
"__id": "purchase:5af1272e-dd26-11e0-b65e-701a04e25543.purchase_order_line-AlhsVDZGoKvJ",
"__id": "purchase:5af12v70-dv70-1v70-bv70-701a04e25v70.purchase_order_line-AlhsVDZGoKvJ",
"__module": "purchase",
"__model": "purchase.order.line",
"__import_module": "sale",
"__import_model": "sale.order.line",
"name": "PC Assemble SC234",
"price_unit": 150.0,
"product_id": ["product:5af1272e-dd26-11e0-b65e-701a04e25543.product_product_3", "[PCSC234] PC Assemble SC234"],
"product_id": ["product:5af12v70-dv70-1v70-bv70-701a04e25v70.product_product_3", "[PCSC234] PC Assemble SC234"],
"product_qty": 1.0,
"product_uom": ["product:5af1272e-dd26-11e0-b65e-701a04e25543.product_uom_unit", "Unit"],
"product_uom": ["product:5af12v70-dv70-1v70-bv70-701a04e25v70.product_uom_unit", "Unit"],
},
{
"__id": "purchase:5af1272e-dd26-11e0-b65e-701a04e25543.purchase_order_line-Alsads33e",
"__id": "purchase:5af12v70-dv70-1v70-bv70-701a04e25v70.purchase_order_line-Alsads33e",
"__module": "purchase",
"__model": "purchase.order.line",
"__import_module": "sale",
"__import_model": "sale.order.line",
"name": "PC on Demand",
"price_unit": 100.0,
"product_id": ["product:5af1272e-dd26-11e0-b65e-701a04e25543.product_product_5", "[PC-DEM] PC on Demand"],
"product_id": ["product:5af12v70-dv70-1v70-bv70-701a04e25v70.product_product_5", "[PC-DEM] PC on Demand"],
"product_qty": 2.0,
"product_uom": ["product:5af1272e-dd26-11e0-b65e-701a04e25543.product_uom_unit", "Unit"],
"product_uom": ["product:5af12v70-dv70-1v70-bv70-701a04e25v70.product_uom_unit", "Unit"],
}],
}
new_sale_order_id = sale_order_pool.edi_import(cr, uid, edi_document, context=context)
@ -96,9 +97,10 @@
order_new = sale_order_pool.browse(cr, uid, new_sale_order_id)
# check bank info on partner
assert order_new.partner_id.customer, "Imported partner should be a customer, as we just imported the document as a sale order"
assert len(order_new.partner_id.bank_ids) == 1, "Expected 1 bank entry related to partner"
bank_info = order_new.partner_id.bank_ids[0]
assert bank_info.acc_number == "Ladies bank: 032465789-156113", 'Expected "Ladies bank: 032465789-156113", got %s' % bank_info.acc_number
assert bank_info.acc_number == "Another bank: 032465700-156700", 'Expected "Another bank: 032465700-156700", got %s' % bank_info.acc_number
assert order_new.pricelist_id.name == 'Public Pricelist' , "Public Price list was not automatically assigned"
assert order_new.amount_total == 350, "Amount total is wrong"
@ -115,3 +117,101 @@
assert sale_line.product_uom_qty == 2 , "product qty is not same"
else:
raise AssertionError('unknown order line: %s' % sale_line)
-
"Then I import a sample EDI document of a purchase order (v6.1 - to test backwards compatibility)"
-
!python {model: edi.edi}: |
sale_order_pool = self.pool.get('sale.order')
edi_document = {
"__id": "purchase:5af1272e-dd26-11e0-b65e-701a04e25543.purchase_order_test",
"__module": "purchase",
"__model": "purchase.order",
"__import_module": "sale",
"__import_model": "sale.order",
"__version": [6,1,0],
"name": "PO00011-v61",
"date_order": "2011-09-12",
"currency": {
"__id": "base:5af1272e-dd26-11e0-b65e-701a04e25543.EUR",
"__module": "base",
"__model": "res.currency",
"code": "EUR",
"symbol": "€",
},
"company_id": ["base:5af1272e-dd26-11e0-b65e-701a04e25543.main_company", "Client S.A."],
"company_address": {
"__id": "base:5af1272e-dd26-11e0-b65e-701a04e25543.some_address",
"__module": "base",
"__model": "res.partner.address",
"phone": "(+32).81.81.37.00",
"street": "Chaussee de Namur 40",
"city": "Gerompont",
"zip": "1367",
"country_id": ["base:5af1272e-dd26-11e0-b65e-701a04e25543.be", "Belgium"],
"bank_ids": [
["base:5af1272e-dd26-11e0-b65e-701a04e25543.res_partner_bank-adaWadsadasdDJzGbp","Ladies bank: 032465789-156113"]
],
},
"partner_id": ["purchase:5af1272e-dd26-11e0-b65e-701a04e25543.res_partner_test20", "jones white"],
"partner_address": {
"__id": "base:5af1272e-dd26-11e0-b65e-701a04e25543.res_partner_address_7wdsjasdjh",
"__module": "base",
"__model": "res.partner.address",
"phone": "(+32).81.81.37.00",
"street": "Chaussee de Namur 40",
"city": "Gerompont",
"zip": "1367",
"country_id": ["base:5af1272e-dd26-11e0-b65e-701a04e25543.be", "Belgium"],
},
"order_line": [{
"__id": "purchase:5af1272e-dd26-11e0-b65e-701a04e25543.purchase_order_line-AlhsVDZGoKvJ",
"__module": "purchase",
"__model": "purchase.order.line",
"__import_module": "sale",
"__import_model": "sale.order.line",
"name": "Basic PC",
"date_planned": "2011-09-30",
"price_unit": 150.0,
"product_id": ["product:5af1272e-dd26-11e0-b65e-701a04e25543.product_product_pc1", "[PC1] Basic PC"],
"product_qty": 1.0,
"product_uom": ["product:5af1272e-dd26-11e0-b65e-701a04e25543.product_uom_unit", "PCE"],
},
{
"__id": "purchase:5af1272e-dd26-11e0-b65e-701a04e25543.purchase_order_line-Alsads33e",
"__module": "purchase",
"__model": "purchase.order.line",
"__import_module": "sale",
"__import_model": "sale.order.line",
"name": "Medium PC",
"date_planned": "2011-09-15",
"price_unit": 100.0,
"product_id": ["product:5af1272e-dd26-11e0-b65e-701a04e25543.product_product_pc3", "[PC3] Medium PC"],
"product_qty": 2.0,
"product_uom": ["product:5af1272e-dd26-11e0-b65e-701a04e25543.product_uom_unit", "PCE"],
}],
}
new_sale_order_id = sale_order_pool.edi_import(cr, uid, edi_document, context=context)
assert new_sale_order_id, 'Sale order import failed'
order_new = sale_order_pool.browse(cr, uid, new_sale_order_id)
# check bank info on partner
assert order_new.partner_id.customer, "Imported partner should be a customer, as we just imported the document as a sale order"
assert len(order_new.partner_id.bank_ids) == 1, "Expected 1 bank entry related to partner"
bank_info = order_new.partner_id.bank_ids[0]
assert bank_info.acc_number == "Ladies bank: 032465789-156113", 'Expected "Ladies bank: 032465789-156113", got %s' % bank_info.acc_number
assert order_new.pricelist_id.name == 'Public Pricelist' , "Public Price list was not automatically assigned"
assert order_new.amount_total == 350, "Amount total is wrong"
assert order_new.amount_untaxed == 350, "Untaxed amount is wrong"
assert len(order_new.order_line) == 2, "Sale order lines mismatch"
for sale_line in order_new.order_line:
if sale_line.name == 'Basic PC':
assert sale_line.product_uom.name == "PCE" , "uom is not same"
assert sale_line.price_unit == 150 , "unit price is not same, got %s, expected 150"%(sale_line.price_unit,)
assert sale_line.product_uom_qty == 1 , "product qty is not same"
elif sale_line.name == 'Medium PC':
assert sale_line.product_uom.name == "PCE" , "uom is not same"
assert sale_line.price_unit == 100 , "unit price is not same, got %s, expected 100"%(sale_line.price_unit,)
assert sale_line.product_uom_qty == 2 , "product qty is not same"
else:
raise AssertionError('unknown order line: %s' % sale_line)