[Merge]with: lp:openobject-addons

bzr revid: dbr@tinyerp.com-20111117064746-mue10buy3i62rio0
This commit is contained in:
DBR (OpenERP)
2011-11-17 12:17:46 +05:30
230 changed files with 5515 additions and 2015 deletions
+1 -1
View File
@@ -35,5 +35,5 @@ import product
import ir_sequence
import company
import res_currency
import edi
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+5 -5
View File
@@ -53,7 +53,7 @@ module named account_voucher.
'website': 'http://www.openerp.com',
'images' : ['images/accounts.jpeg','images/bank_statement.jpeg','images/cash_register.jpeg','images/chart_of_accounts.jpeg','images/customer_invoice.jpeg','images/journal_entries.jpeg'],
'init_xml': [],
"depends" : ["base_setup", "product", "analytic", "process","board"],
"depends" : ["base_setup", "product", "analytic", "process", "board", "edi"],
'update_xml': [
'security/account_security.xml',
'security/ir.model.access.csv',
@@ -123,7 +123,8 @@ module named account_voucher.
'board_account_view.xml',
"wizard/account_report_profit_loss_view.xml",
"wizard/account_report_balance_sheet_view.xml",
"account_bank_view.xml"
"edi/invoice_action_data.xml",
"account_bank_view.xml",
],
'demo_xml': [
'demo/account_demo.xml',
@@ -145,10 +146,9 @@ module named account_voucher.
'test/account_fiscalyear_close.yml',
'test/account_bank_statement.yml',
'test/account_cash_statement.yml',
'test/test_edi_invoice.yml',
'test/account_report.yml',
],
],
'installable': True,
'active': False,
'certificate': '0080331923549',
+18 -16
View File
@@ -404,7 +404,7 @@ class account_account(osv.osv):
return True
_columns = {
'name': fields.char('Name', size=128, required=True, select=True),
'name': fields.char('Name', size=256, required=True, select=True),
'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Forces all moves for this account to have this secondary currency."),
'code': fields.char('Code', size=64, required=True, select=1),
'type': fields.selection([
@@ -431,9 +431,9 @@ class account_account(osv.osv):
'debit': fields.function(__compute, fnct_inv=_set_credit_debit, digits_compute=dp.get_precision('Account'), string='Debit', multi='balance'),
'foreign_balance': fields.function(__compute, digits_compute=dp.get_precision('Account'), string='Foreign Balance', multi='balance',
help="Total amount (in Secondary currency) for transactions held in secondary currency for this account."),
'adjusted_balance': fields.function(__compute, digits_compute=dp.get_precision('Account'), string='Adjusted Balance', multi='balance',
'adjusted_balance': fields.function(__compute, digits_compute=dp.get_precision('Account'), string='Adjusted Balance', multi='balance',
help="Total amount (in Company currency) for transactions held in secondary currency for this account."),
'unrealized_gain_loss': fields.function(__compute, digits_compute=dp.get_precision('Account'), string='Unrealized Gain or Loss', multi='balance',
'unrealized_gain_loss': fields.function(__compute, digits_compute=dp.get_precision('Account'), string='Unrealized Gain or Loss', multi='balance',
help="Value of Loss or Gain due to changes in exchange rate when doing multi-currency transactions."),
'reconcile': fields.boolean('Allow Reconciliation', help="Check this box if this account allows reconciliation of journal items."),
'exchange_rate': fields.related('currency_id', 'rate', type='float', string='Exchange Rate', digits=(12,6)),
@@ -2001,8 +2001,11 @@ class account_tax(osv.osv):
cur_price_unit+=amount2
return res
def compute_all(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
def compute_all(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None, force_excluded=False):
"""
:param force_excluded: boolean used to say that we don't want to consider the value of field price_include of
tax. It's used in encoding by line where you don't matter if you encoded a tax with that boolean to True or
False
RETURN: {
'total': 0.0, # Total without taxes
'total_included: 0.0, # Total with taxes
@@ -2014,10 +2017,10 @@ class account_tax(osv.osv):
tin = []
tex = []
for tax in taxes:
if tax.price_include:
tin.append(tax)
else:
if not tax.price_include or force_excluded:
tex.append(tax)
else:
tin.append(tax)
tin = self.compute_inv(cr, uid, tin, price_unit, quantity, address_id=address_id, product=product, partner=partner)
for r in tin:
totalex -= r.get('amount', 0.0)
@@ -2183,6 +2186,7 @@ class account_model(osv.osv):
account_move_obj = self.pool.get('account.move')
account_move_line_obj = self.pool.get('account.move.line')
pt_obj = self.pool.get('account.payment.term')
period_obj = self.pool.get('account.period')
if context is None:
context = {}
@@ -2190,14 +2194,14 @@ class account_model(osv.osv):
if datas.get('date', False):
context.update({'date': datas['date']})
period_id = self.pool.get('account.period').find(cr, uid, dt=context.get('date', False))
if not period_id:
raise osv.except_osv(_('No period found !'), _('Unable to find a valid period !'))
period_id = period_id[0]
move_date = context.get('date', time.strftime('%Y-%m-%d'))
move_date = datetime.strptime(move_date,"%Y-%m-%d")
for model in self.browse(cr, uid, ids, context=context):
ctx = context.copy()
ctx.update({'company_id': model.company_id.id})
period_ids = period_obj.find(cr, uid, dt=context.get('date', False), context=ctx)
period_id = period_ids and period_ids[0] or False
ctx.update({'journal_id': model.journal_id.id,'period_id': period_id})
try:
entry['name'] = model.name%{'year': move_date.strftime('%Y'), 'month': move_date.strftime('%m'), 'date': move_date.strftime('%Y-%m')}
except:
@@ -2246,9 +2250,7 @@ class account_model(osv.osv):
'date': context.get('date',time.strftime('%Y-%m-%d')),
'date_maturity': date_maturity
})
c = context.copy()
c.update({'journal_id': model.journal_id.id,'period_id': period_id})
account_move_line_obj.create(cr, uid, val, context=c)
account_move_line_obj.create(cr, uid, val, context=ctx)
return move_ids
@@ -2395,7 +2397,7 @@ class account_account_template(osv.osv):
_description ='Templates for Accounts'
_columns = {
'name': fields.char('Name', size=128, required=True, select=True),
'name': fields.char('Name', size=256, required=True, select=True),
'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Forces all moves for this account to have this secondary currency."),
'code': fields.char('Code', size=64, select=1),
'type': fields.selection([
+4
View File
@@ -42,8 +42,12 @@ class bank(osv.osv):
return (bank.bank_name or '') + ' ' + bank.acc_number
def post_write(self, cr, uid, ids, context={}):
if isinstance(ids, (int, long)):
ids = [ids]
obj_acc = self.pool.get('account.account')
obj_data = self.pool.get('ir.model.data')
for bank in self.browse(cr, uid, ids, context):
if bank.company_id and not bank.journal_id:
# Find the code and parent of the bank account to create
+7 -7
View File
@@ -251,7 +251,7 @@ class account_invoice(osv.osv):
'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}),
'journal_id': fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}),
'company_id': fields.many2one('res.company', 'Company', required=True, change_default=True, readonly=True, states={'draft':[('readonly',False)]}),
'check_total': fields.float('Total', digits_compute=dp.get_precision('Account'), states={'open':[('readonly',True)],'close':[('readonly',True)]}),
'check_total': fields.float('Verification Total', digits_compute=dp.get_precision('Account'), states={'open':[('readonly',True)],'close':[('readonly',True)]}),
'reconciled': fields.function(_reconciled, string='Paid/Reconciled', type='boolean',
store={
'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 50), # Check if we can remove ?
@@ -795,6 +795,7 @@ class account_invoice(osv.osv):
"""Creates invoice related analytics and financial move lines"""
ait_obj = self.pool.get('account.invoice.tax')
cur_obj = self.pool.get('res.currency')
period_obj = self.pool.get('account.period')
context = {}
for inv in self.browse(cr, uid, ids):
if not inv.journal_id.sequence_id:
@@ -923,10 +924,10 @@ class account_invoice(osv.osv):
'narration':inv.comment
}
period_id = inv.period_id and inv.period_id.id or False
ctx.update({'company_id': inv.company_id.id})
if not period_id:
period_ids = self.pool.get('account.period').search(cr, uid, [('date_start','<=',inv.date_invoice or time.strftime('%Y-%m-%d')),('date_stop','>=',inv.date_invoice or time.strftime('%Y-%m-%d')), ('company_id', '=', inv.company_id.id)])
if period_ids:
period_id = period_ids[0]
period_ids = period_obj.find(cr, uid, inv.date_invoice, context=ctx)
period_id = period_ids and period_ids[0] or False
if period_id:
move['period_id'] = period_id
for i in line:
@@ -1323,9 +1324,9 @@ class account_invoice_line(osv.osv):
raise osv.except_osv(_('No Partner Defined !'),_("You must first select a partner !") )
if not product:
if type in ('in_invoice', 'in_refund'):
return {'value': {'categ_id': False}, 'domain':{'product_uom':[]}}
return {'value': {}, 'domain':{'product_uom':[]}}
else:
return {'value': {'price_unit': 0.0, 'categ_id': False}, 'domain':{'product_uom':[]}}
return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}}
part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
fpos_obj = self.pool.get('account.fiscal.position')
fpos = fposition_id and fpos_obj.browse(cr, uid, fposition_id, context=context) or False
@@ -1378,7 +1379,6 @@ class account_invoice_line(osv.osv):
if res2:
domain = {'uos_id':[('category_id','=',res2 )]}
result['categ_id'] = res.categ_id.id
res_final = {'value':result, 'domain':domain}
if not company_id or not currency_id:
+7 -7
View File
@@ -1084,8 +1084,6 @@ class account_move_line(osv.osv):
f.set("invisible", "context.get('journal_id', False)")
elif field in ('period_id',):
f.set("invisible", "context.get('period_id', False)")
else:
f.set('invisible', "context.get('visible_id') not in %s" % (fields.get(field)))
orm.setup_modifiers(f, fields_get[field], context=context,
in_tree_view=True)
@@ -1245,6 +1243,12 @@ class account_move_line(osv.osv):
m = move_obj.browse(cr, uid, vals['move_id'])
context['journal_id'] = m.journal_id.id
context['period_id'] = m.period_id.id
#we need to treat the case where a value is given in the context for period_id as a string
if 'period_id' not in context or not isinstance(context.get('period_id', ''), (int, long)):
period_candidate_ids = self.pool.get('account.period').name_search(cr, uid, name=context.get('period_id',''))
if len(period_candidate_ids) != 1:
raise osv.except_osv(_('Encoding error'), _('No period found or period given is ambigous.'))
context['period_id'] = period_candidate_ids[0][0]
self._update_journal_check(cr, uid, context['journal_id'], context['period_id'], context)
move_id = vals.get('move_id', False)
journal = journal_obj.browse(cr, uid, context['journal_id'], context=context)
@@ -1326,7 +1330,7 @@ class account_move_line(osv.osv):
base_sign = 'base_sign'
tax_sign = 'tax_sign'
tmp_cnt = 0
for tax in tax_obj.compute_all(cr, uid, [tax_id], total, 1.00).get('taxes'):
for tax in tax_obj.compute_all(cr, uid, [tax_id], total, 1.00, force_excluded=True).get('taxes'):
#create the base movement
if tmp_cnt == 0:
if tax[base_code]:
@@ -1338,8 +1342,6 @@ class account_move_line(osv.osv):
else:
data = {
'move_id': vals['move_id'],
'journal_id': vals['journal_id'],
'period_id': vals['period_id'],
'name': tools.ustr(vals['name'] or '') + ' ' + tools.ustr(tax['name'] or ''),
'date': vals['date'],
'partner_id': vals.get('partner_id',False),
@@ -1356,8 +1358,6 @@ class account_move_line(osv.osv):
#create the VAT movement
data = {
'move_id': vals['move_id'],
'journal_id': vals['journal_id'],
'period_id': vals['period_id'],
'name': tools.ustr(vals['name'] or '') + ' ' + tools.ustr(tax['name'] or ''),
'date': vals['date'],
'partner_id': vals.get('partner_id',False),
+1
View File
@@ -19,6 +19,7 @@
rml="account/report/account_print_invoice.rml"
string="Invoices"
attachment="(object.state in ('open','paid')) and ('INV'+(object.number or '').replace('/',''))"
usage="default"
multi="True"/>
<report id="account_transfers" model="account.transfer" name="account.transfer" string="Transfers" xml="account/report/transfer.xml" xsl="account/report/transfer.xsl"/>
<report auto="False" id="account_intracom" menu="False" model="account.move.line" name="account.intracom" string="IntraCom"/>
+5 -7
View File
@@ -1235,8 +1235,8 @@
</group>
<newline/>
<group>
<field name="journal_id" widget="selection" context="{'journal_id':self, 'visible_id':self, 'normal_view':False}"/>
<field name="period_id" context="{'period_id':self, 'search_default_period_id':self}"/>
<field name="journal_id" widget="selection" context="{'journal_id':self}"/>
<field name="period_id" context="{'period_id':self}"/>
</group>
<newline/>
<group expand="0" string="Group By...">
@@ -1306,7 +1306,6 @@
<field name="model">account.account</field>
<field name="name">Open Journal Items</field>
<field eval="'ir.actions.act_window,%d'%action_move_line_select" name="value"/>
<field eval="True" name="object"/>
</record>
<!--
Account.Entry Edition
@@ -1626,7 +1625,7 @@
<tree string="Journal Entry Model Line" editable="bottom">
<field name="sequence"/>
<field name="name"/>
<field name="account_id"/>
<field name="account_id" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation'), ('company_id', '=', parent.company_id)]"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<field name="partner_id"/>
<field name="debit"/>
@@ -1645,12 +1644,12 @@
<form string="Journal Entry Model Line">
<field colspan="4" name="name" select="1"/>
<field name="sequence"/>
<field name="account_id" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
<field name="account_id" domain="[('type','&lt;&gt;','view'), ('type','&lt;&gt;','consolidation'), ('company_id', '=', parent.company_id)]"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<field name="partner_id"/>
<field name="debit" select="1"/>
<field name="credit" select="1"/>
<field name="quantity"/>
<field name="quantity"/>
<field name="date_maturity"/>
</form>
</field>
@@ -2017,7 +2016,6 @@
<field name="model">account.tax.code</field>
<field name="name">Tax Details</field>
<field eval="'ir.actions.act_window,%d'%action_tax_code_line_open" name="value"/>
<field eval="True" name="object"/>
</record>
+10 -11
View File
@@ -38,17 +38,16 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Account Board">
<hpaned>
<child1>
<action colspan="4" height="160" width="400" name="%(account.action_invoice_tree1)d" string="Customer Invoices to Approve" domain="[('state','in',('draft','proforma2')), ('type','=','out_invoice')]"/>
<action colspan="4" height="160" width="400" name="%(action_company_analysis_tree)d" string="Company Analysis" groups="account.group_account_manager"/>
</child1>
<child2>
<action colspan="4" height="220" name="%(action_treasory_graph)d" string="Treasury" groups="account.group_account_manager,account.group_account_user"/>
<action colspan="4" height="220" name="%(action_aged_receivable)d" string="Aged Receivables" groups="account.group_account_manager,account.group_account_user"/>
<!-- <action colspan="4" height="220" name="%(action_aged_income)d" string="Aged income"/> -->
</child2>
</hpaned>
<board style="2-1">
<column>
<action name="%(account.action_invoice_tree1)d" string="Customer Invoices to Approve" domain="[('state','in',('draft','proforma2')), ('type','=','out_invoice')]"/>
<action name="%(action_company_analysis_tree)d" string="Company Analysis"/>
</column>
<column>
<action name="%(action_treasory_graph)d" string="Treasury"/> <!--groups="account.group_account_manager,account.group_account_user"-->
<action name="%(action_aged_receivable)d" string="Aged Receivables"/> <!--groups="account.group_account_manager,account.group_account_user"-->
</column>
</board>
</form>
</field>
</record>
+1
View File
@@ -24,6 +24,7 @@ from osv import fields, osv
class res_company(osv.osv):
_inherit = "res.company"
_columns = {
'paypal_account': fields.char("Paypal Account", size=128, help="Paypal username (usually email) for receiving online payments."),
'overdue_msg': fields.text('Overdue Payments Message', translate=True),
'property_reserve_and_surplus_account': fields.property(
'account.account',
+1
View File
@@ -24,6 +24,7 @@
<field name="arch" type="xml">
<field name="currency_id" position="after">
<field name="property_reserve_and_surplus_account" colspan="2"/>
<field name="paypal_account" />
</field>
</field>
</record>
+22
View File
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2011 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 invoice
+273
View File
@@ -0,0 +1,273 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2011 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 osv import fields, osv, orm
from edi import EDIMixin
INVOICE_LINE_EDI_STRUCT = {
'name': True,
'origin': True,
'uos_id': True,
'product_id': True,
'price_unit': True,
'quantity': True,
'discount': True,
'note': True,
# fields used for web preview only - discarded on import
'price_subtotal': True,
}
INVOICE_TAX_LINE_EDI_STRUCT = {
'name': True,
'base': True,
'amount': True,
'manual': True,
'sequence': True,
'base_amount': True,
'tax_amount': True,
}
INVOICE_EDI_STRUCT = {
'name': True,
'origin': True,
'company_id': True, # -> to be changed into partner
'type': True, # -> reversed at import
'internal_number': True, # -> reference at import
'comment': True,
'date_invoice': True,
'date_due': True,
'partner_id': True,
'payment_term': True,
#custom: currency_id
'invoice_line': INVOICE_LINE_EDI_STRUCT,
'tax_line': INVOICE_TAX_LINE_EDI_STRUCT,
# fields used for web preview only - discarded on import
#custom: 'partner_ref'
'amount_total': True,
'amount_untaxed': True,
'amount_tax': True,
}
class account_invoice(osv.osv, EDIMixin):
_inherit = 'account.invoice'
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)
res_company = self.pool.get('res.company')
res_partner_address = self.pool.get('res.partner.address')
edi_doc_list = []
for invoice in records:
# generate the main report
self._edi_generate_report_attachment(cr, uid, invoice, context=context)
edi_doc = super(account_invoice,self).edi_export(cr, uid, [invoice], edi_struct, context)[0]
edi_doc.update({
'company_address': res_company.edi_export_address(cr, uid, invoice.company_id, context=context),
'company_paypal_account': invoice.company_id.paypal_account,
'partner_address': res_partner_address.edi_export(cr, uid, [invoice.address_invoice_id], context=context)[0],
'currency': self.pool.get('res.currency').edi_export(cr, uid, [invoice.currency_id], context=context)[0],
'partner_ref': invoice.reference or False,
})
edi_doc_list.append(edi_doc)
return edi_doc_list
def _edi_tax_account(self, cr, uid, invoice_type='out_invoice', context=None):
#TODO/FIXME: should select proper Tax Account
account_pool = self.pool.get('account.account')
account_ids = account_pool.search(cr, uid, [('type','<>','view'),('type','<>','income'), ('type', '<>', 'closed')])
tax_account = False
if account_ids:
tax_account = account_pool.browse(cr, uid, account_ids[0])
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)
if invoice_type in ('out_invoice', 'out_refund'):
invoice_account = partner.property_account_receivable
else:
invoice_account = partner.property_account_payable
return invoice_account
def _edi_product_account(self, cr, uid, product_id, invoice_type, context=None):
product_pool = self.pool.get('product.product')
product = product_pool.browse(cr, uid, product_id, context=context)
if invoice_type in ('out_invoice','out_refund'):
account = product.property_account_income or product.categ_id.property_account_income_categ
else:
account = product.property_account_expense or product.categ_id.property_account_expense_categ
return account
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','type'), edi_document)
res_partner_address = self.pool.get('res.partner.address')
res_partner = self.pool.get('res.partner')
# imported company = new partner
src_company_id, src_company_name = edi_document.pop('company_id')
partner_id = self.edi_import_relation(cr, uid, 'res.partner', src_company_name,
src_company_id, context=context)
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})
res_partner.write(cr, uid, [partner_id], partner_value, context=context)
# imported company_address = new partner address
address_info = edi_document.pop('company_address')
address_info['partner_id'] = (src_company_id, src_company_name)
address_info['type'] = 'invoice'
address_id = res_partner_address.edi_import(cr, uid, address_info, context=context)
# modify edi_document to refer to new partner
partner_address = res_partner_address.browse(cr, uid, address_id, context=context)
edi_document['partner_id'] = (src_company_id, src_company_name)
edi_document.pop('partner_address', False) # ignored
edi_document['address_invoice_id'] = self.edi_m2o(cr, uid, partner_address, context=context)
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
a new partner (e.g. supplier company for a customer invoice will be come a supplier
record for the new invoice.
Summary of tasks that need to be done:
- import company as a new partner, if type==in then supplier=1, else customer=1
- partner_id field is modified to point to the new partner
- company_address data used to add address to new partner
- change type: out_invoice'<->'in_invoice','out_refund'<->'in_refund'
- reference: should contain the value of the 'internal_number'
- reference_type: 'none'
- internal number: reset to False, auto-generated
- journal_id: should be selected based on type: simply put the 'type'
in the context when calling create(), will be selected correctly
- payment_term: if set, create a default one based on name...
- for invoice lines, the account_id value should be taken from the
product's default, i.e. from the default category, as it will not
be provided.
- for tax lines, we disconnect from the invoice.line, so all tax lines
will be of type 'manual', and default accounts should be picked based
on the tax config of the DB where it is imported.
"""
if context is None:
context = {}
self._edi_requires_attributes(('company_id','company_address','type','invoice_line','currency'), edi_document)
# extract currency info
res_currency = self.pool.get('res.currency')
currency_info = edi_document.pop('currency')
currency_id = res_currency.edi_import(cr, uid, currency_info, context=context)
currency = res_currency.browse(cr, uid, currency_id)
edi_document['currency_id'] = self.edi_m2o(cr, uid, currency, context=context)
# change type: out_invoice'<->'in_invoice','out_refund'<->'in_refund'
invoice_type = edi_document['type']
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
partner_id = self._edi_import_company(cr, uid, edi_document, context=context)
# Set Account
invoice_account = self._edi_invoice_account(cr, uid, partner_id, invoice_type, context=context)
edi_document['account_id'] = invoice_account and self.edi_m2o(cr, uid, invoice_account, context=context) or False
# reference: should contain the value of the 'internal_number'
edi_document['reference'] = edi_document.get('internal_number', False)
# reference_type: 'none'
edi_document['reference_type'] = 'none'
# internal number: reset to False, auto-generated
edi_document['internal_number'] = False
# discard web preview fields, if present
edi_document.pop('partner_ref', None)
# journal_id: should be selected based on type: simply put the 'type' in the context when calling create(), will be selected correctly
context.update(type=invoice_type)
# for invoice lines, the account_id value should be taken from the product's default, i.e. from the default category, as it will not be provided.
for edi_invoice_line in edi_document['invoice_line']:
product_info = edi_invoice_line['product_id']
product_id = self.edi_import_relation(cr, uid, 'product.product', product_info[1],
product_info[0], context=context)
account = self._edi_product_account(cr, uid, product_id, invoice_type, context=context)
# TODO: could be improved with fiscal positions perhaps
# account = fpos_obj.map_account(cr, uid, fiscal_position_id, account.id)
edi_invoice_line['account_id'] = self.edi_m2o(cr, uid, account, context=context) if account else False
# discard web preview fields, if present
edi_invoice_line.pop('price_subtotal', None)
# for tax lines, we disconnect from the invoice.line, so all tax lines will be of type 'manual', and default accounts should be picked based
# on the tax config of the DB where it is imported.
tax_account = self._edi_tax_account(cr, uid, context=context)
tax_account_info = self.edi_m2o(cr, uid, tax_account, context=context)
for edi_tax_line in edi_document.get('tax_line', []):
edi_tax_line['account_id'] = tax_account_info
edi_tax_line['manual'] = True
return super(account_invoice,self).edi_import(cr, uid, edi_document, context=context)
def _edi_record_display_action(self, cr, uid, id, context=None):
"""Returns an appropriate action definition dict for displaying
the record with ID ``rec_id``.
:param int id: database ID of record to display
:return: action definition dict
"""
action = super(account_invoice,self)._edi_record_display_action(cr, uid, id, context=context)
try:
invoice = self.browse(cr, uid, id, context=context)
if 'out_' in invoice.type:
view_ext_id = 'invoice_form'
journal_type = 'sale'
else:
view_ext_id = 'invoice_supplier_form'
journal_type = 'purchase'
ctx = "{'type': '%s', 'journal_type': '%s'}" % (invoice.type, journal_type)
action.update(context=ctx)
view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', view_ext_id)[1]
action.update(views=[(view_id,'form'), (False, 'tree')])
except ValueError:
# ignore if views are missing
pass
return action
class account_invoice_line(osv.osv, EDIMixin):
_inherit='account.invoice.line'
class account_invoice_tax(osv.osv, EDIMixin):
_inherit = "account.invoice.tax"
+186
View File
@@ -0,0 +1,186 @@
<?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>
<field name="res_model">email.template</field>
<field name="view_type">form</field>
<field name="view_mode">form,tree</field>
<field name="view_id" ref="email_template.email_template_tree" />
<field name="search_view_id" ref="email_template.view_email_template_search"/>
<field name="context">{'search_default_model_id':'account.invoice'}</field>
<field name="context" eval="{'search_default_model_id': ref('account.model_account_invoice')}"/>
</record>
<menuitem id="menu_email_templates" parent="menu_configuration_misc" action="action_email_templates" sequence="30"/>
</data>
<!-- Mail template and workflow bindings are done 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="email_from">${object.user_id.user_email or object.company_id.email or 'noreply@localhost'}</field>
<field name="subject">${object.company_id.name} Invoice (Ref ${object.number or 'n/a' })</field>
<field name="email_to">${object.address_invoice_id.email or ''}</field>
<field name="model_id" ref="account.model_account_invoice"/>
<field name="auto_delete" eval="True"/>
<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.address_invoice_id.name and ' ' or ''}${object.address_invoice_id.name or ''},</p>
<p>A new invoice is available for ${object.partner_id.name}: </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
&nbsp;&nbsp;Your contact: <a href="mailto:${object.user_id.user_email or ''}?subject=Invoice%20${object.number}">${object.user_id.name}</a>
</p>
<p>
You can view the invoice document, download it and pay online using the following link:
</p>
<a style="display:block; width: 150px; height:20px; margin-left: 120px; color: #FFF; font-family: 'Lucida Grande', Helvetica, Arial, sans-serif; font-size: 13px; font-weight: bold; text-align: center; text-decoration: none !important; line-height: 1; padding: 5px 0px 0px 0px; background-color: #8E0000; border-radius: 5px 5px; background-repeat: repeat no-repeat;"
href="${ctx.get('edi_web_url_view') or ''}">View Invoice</a>
% if object.company_id.paypal_account and object.type in ('out_invoice', 'in_refund'):
<%
comp_name = quote(object.company_id.name)
inv_number = quote(object.number)
paypal_account = quote(object.company_id.paypal_account)
inv_amount = quote(str(object.amount_total))
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>
<field name="body_text"><![CDATA[
Hello${object.address_invoice_id.name and ' ' or ''}${object.address_invoice_id.name or ''},
A new invoice is available for ${object.partner_id.name}:
| Invoice number: *${object.number}*
| Invoice total: *${object.amount_total} ${object.currency_id.name}*
| Invoice date: ${object.date_invoice}
% if object.origin:
| Order reference: ${object.origin}
% endif
| Your contact: ${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''}
You can view the invoice document, download it and pay online using the following link:
${ctx.get('edi_web_url_view') or 'n/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))
cur_name = quote(object.currency_id.name)
paypal_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=%s%%20Invoice%%20%s"\
"&invoice=%s&amount=%s&currency_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s" % \
(paypal_account,comp_name,inv_number,inv_number,inv_amount,cur_name,cur_name)
%>
It is also possible to directly pay with Paypal:
${paypal_url}
% endif
If you have any question, do not hesitate to contact us.
Thank you for choosing ${object.company_id.name}!
--
${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''}
${object.company_id.name}
% if object.company_id.street:
${object.company_id.street or ''}
% endif
% if object.company_id.street2:
${object.company_id.street2}
% endif
% if object.company_id.city or object.company_id.zip:
${object.company_id.zip or ''} ${object.company_id.city or ''}
% 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 ''}
% endif
% if object.company_id.phone:
Phone: ${object.company_id.phone}
% endif
% if object.company_id.website:
${object.company_id.website or ''}
% endif
]]></field>
</record>
</data>
</openerp>
+264 -237
View File
File diff suppressed because it is too large Load Diff
+12 -10
View File
@@ -91,15 +91,18 @@
</group>
<notebook colspan="4">
<page string="Account Data">
<field name="partner_id"/>
<field name="contact_id"/>
<field name="currency_id"/>
<newline/>
<field name="date_start"/>
<field name="date"/>
<newline/>
<field name="quantity_max"/>
<field name="user_id"/>
<group colspan="2" col="2">
<separator colspan="2" string="Contacts"/>
<field name="partner_id" on_change="on_change_partner_id(partner_id)"/>
<field name="contact_id"/>
<field name="user_id"/>
</group>
<group colspan="2" col="2" name="contract">
<separator colspan="2" string="Contract Data"/>
<field name="date_start"/>
<field name="date"/>
<field name="quantity_max"/>
</group>
</page>
<page string="Description">
<field colspan="4" name="description" nolabel="1"/>
@@ -255,7 +258,6 @@
<field eval="'account.analytic.account'" name="model"/>
<field name="name">Open Account Tree</field>
<field eval="'ir.actions.act_window,%d'%action_account_tree1" name="value"/>
<field eval="True" name="object"/>
</record>
<record id="account_analytic_line_extended_form" model="ir.ui.view">
@@ -35,7 +35,6 @@
<record model="ir.values" id="account_analytic_balance_values">
<field name="model_id" ref="analytic.model_account_analytic_account" />
<field name="object" eval="1" />
<field name="name">Account Analytic Balance</field>
<field name="key2">client_print_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_account_analytic_balance'))" />
@@ -34,7 +34,6 @@
<record model="ir.values" id="account_analytic_cost_ledger_journal_values">
<field name="model_id" ref="analytic.model_account_analytic_account" />
<field name="object" eval="1" />
<field name="name">Account Analytic Cost Ledger Journal</field>
<field name="key2">client_print_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_account_analytic_cost_ledger_journal'))" />
@@ -43,4 +42,4 @@
</record>
</data>
</openerp>
</openerp>
@@ -33,7 +33,6 @@
<record model="ir.values" id="account_analytic_cost_values">
<field name="model_id" ref="analytic.model_account_analytic_account" />
<field name="object" eval="1" />
<field name="name">Account Analytic Cost</field>
<field name="key2">client_print_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_account_analytic_cost'))" />
@@ -42,4 +41,4 @@
</record>
</data>
</openerp>
</openerp>
@@ -33,7 +33,6 @@
<record model="ir.values" id="account_analytic_invert_balance_values">
<field name="model_id" ref="analytic.model_account_analytic_account" />
<field name="object" eval="1" />
<field name="name">Account Analytic Inverted Balance</field>
<field name="key2">client_print_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_account_analytic_invert_balance'))" />
@@ -33,7 +33,6 @@
<record model="ir.values" id="account_analytic_journal_values">
<field name="model_id" ref="account.model_account_analytic_journal" />
<field name="object" eval="1" />
<field name="name">Account Analytic Journal</field>
<field name="key2">client_print_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_account_analytic_journal'))" />
@@ -5,19 +5,9 @@
-
!python {model: account.bank.statement}: |
import time
period = self._get_period(cr, uid, {'lang': u'en_US', 'tz': False, 'active_model': 'ir.ui.menu',
'journal_type': 'bank', 'period_id':time.strftime('%m'), 'active_ids': [ref('menu_bank_statement_tree')], 'active_id': ref('menu_bank_statement_tree')})
assert period, _('Period has not been selected')
journal = self._default_journal_id(cr, uid, {'lang': u'en_US', 'tz': False, 'active_model': 'ir.ui.menu',
'journal_type': 'bank', 'period_id': time.strftime('%m'), 'active_ids': [ref('menu_bank_statement_tree')], 'active_id': ref('menu_bank_statement_tree')})
assert journal, _('Journal has not been selected')
-
I select the date
-
!python {model: account.bank.statement}: |
import time
date = self.onchange_date(cr, uid, [], time.strftime('%Y-%m-%d'), context=None)
assert date, _('Date has not been selected')
-
I create a bank statement with Opening and Closing balance 0.
-
@@ -11,66 +11,19 @@
footer: True
bank: base.res_bank_1
bank_name: Reserve
-
I select the journal for customer invoice
-
!python {model: account.invoice}: |
journal = self.onchange_journal_id(cr, uid, [], journal_id=ref('account.sales_journal'))
-
I select the partner for customer invoice
-
!python {model: account.invoice}: |
partner = self.onchange_partner_id(cr, uid, [], 'out_invoice', ref('base.res_partner_3'), date_invoice=False,
payment_term=ref('account.account_payment_term_advance'), partner_bank_id=ref('res_partner_bank_0'), company_id=ref('base.main_company'))
-
I select the company for invoice
-
!python {model: account.invoice}: |
company = self.onchange_company_id(cr, uid, [], ref('base.main_company'), False, 'out_invoice', [], ref('base.EUR'))
-
I create a customer invoice
-
!record {model: account.invoice, id: account_invoice_customer0}:
account_id: account.a_recv
address_contact_id: base.res_partner_address_zen
address_invoice_id: base.res_partner_address_zen
company_id: base.main_company
currency_id: base.EUR
date_invoice: !eval time.strftime('%Y-%m-%d')
!record {model: account.invoice, id: account_invoice_customer0, view: invoice_form}:
payment_term: account.account_payment_term_advance
partner_bank_id: res_partner_bank_0
journal_id: account.sales_journal
partner_id: base.res_partner_3
reference_type: none
name: 'Test Customer Invoice'
-
I select a product for invoice
-
!python {model: account.invoice.line}: |
product = self.product_id_change(cr, uid, [], ref("product.product_product_pc3"), False, qty=10.0, name='[PC3] Medium PC',
type='out_invoice', partner_id=ref('base.res_partner_3'), fposition_id=False, price_unit= 900.0, address_invoice_id=ref('base.res_partner_address_zen'),
currency_id=ref('base.EUR'), context={'lang': u'en_US', 'tz': False, 'type': 'out_invoice'}, company_id=ref('base.main_company'))
line = {}
line.update(product.get('value', {}))
line.update({
'quantity': 10.0,
'invoice_id': ref("account_invoice_customer0"),
})
invoice_line = self.create(cr, uid, line)
-
I select product Uos
-
!python {model: account.invoice.line}: |
inv_line_id = self.search(cr, uid, [('invoice_id','=',ref('account_invoice_customer0'))])
uos = self.uos_id_change(cr, uid, inv_line_id, ref("product.product_product_pc3"), ref('product.product_uom_unit'), qty=0, name='[PC3] Medium PC', type='out_invoice',
partner_id=ref('base.res_partner_3'), fposition_id=False, price_unit=False, address_invoice_id=ref('base.res_partner_address_zen'),
currency_id=ref('base.EUR'), context={'lang': u'en_US', 'tz': False, 'type': 'out_invoice'}, company_id=ref('base.main_company'))
-
I select the account for invoice line
-
!python {model: account.invoice.line}: |
inv_line_id = self.search(cr, uid, [('invoice_id','=',ref('account_invoice_customer0'))])
account = self.onchange_account_id(cr, uid, inv_line_id, False, ref('account.a_sale'))
invoice_line:
- product_id: product.product_product_pc3
quantity: 10.0
-
I manually assign tax on invoice
-
+1 -4
View File
@@ -38,7 +38,7 @@
Then I click on the 'Post' button of Journal Entries
-
!python {model: account.move}: |
ids = [ref('account_model_mymodelonyears0')]
ids = self.search(cr, uid, [('ref', '=', 'My Test Model')])
self.button_validate(cr, uid, ids, {})
moves = self.browse(cr, uid, ids)[0]
assert(moves.state == 'posted'), _('Journal Entries are not in posted state')
@@ -48,9 +48,6 @@
!record {model: account.subscription, id: test_recurring_lines }:
name: Test Recurring Lines
model_id: account_model_mymodelonyears0
date_start: !eval time.strftime('%Y-%m-%d')
period_nbr: 1
period_total: 12
period_type: 'day'
-
Then I click on the 'Compute' button of Recurring lines
+152
View File
@@ -0,0 +1,152 @@
-
In order to test the EDI export features of Invoices
-
First I create a draft customer invoice
-
!record {model: account.invoice, id: invoice_edi_1}:
journal_id: 1
partner_id: base.res_partner_agrolait
currency_id: base.EUR
address_invoice_id: base.res_partner_address_8invoice
company_id: 1
account_id: account.a_pay
date_invoice: '2011-06-22'
name: selling product
type: 'out_invoice'
invoice_line:
- product_id: product.product_product_pc1
uos_id: 1
quantity: 1.0
price_unit: 10.0
name: 'basic pc'
account_id: account.a_pay
invoice_line:
- product_id: product.product_product_pc3
uos_id: 1
quantity: 5.0
price_unit: 100.0
name: 'Medium PC'
account_id: account.a_pay
tax_line:
- name: sale tax
account_id: account.a_pay
manual: True
amount: 1000.00
-
I confirm and open the invoice
-
!workflow {model: account.invoice, ref: invoice_edi_1, action: invoice_open}
-
Then I export the customer invoice
-
!python {model: edi.document}: |
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'
-
Then I import a sample EDI document of another customer invoice
-
!python {model: account.invoice}: |
edi_document = {
"__id": "account:b22acf7a-ddcd-11e0-a4db-701a04e25543.random_invoice_763jsms",
"__module": "account",
"__model": "account.invoice",
"__version": [6,1,0],
"internal_number": "SAJ/2011/002",
"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": "2011-06-22",
"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 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 == "SAJ/2011/002", "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"
+1
View File
@@ -66,6 +66,7 @@ import account_change_currency
import account_report_balance_sheet
import account_report_profit_loss
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -32,7 +32,6 @@
<field name="model">account.journal.period</field>
<field name="name">Open Journal</field>
<field eval="'ir.actions.act_window,%d'%action_open_journal_button" name="value"/>
<field eval="True" name="object"/>
</record>
</data>
</openerp>
@@ -188,7 +188,7 @@ class account_move_journal(osv.osv_memory):
'view_mode': 'tree,graph,form',
'res_model': 'account.move.line',
'view_id': False,
'context': "{'search_default_posted': %d, 'visible_id':%s, 'search_default_journal_id':%d, 'search_default_period_id':%d}" % (move, journal_id, journal_id, period_id),
'context': "{'search_default_posted': %d, 'search_default_journal_id':%d, 'search_default_period_id':%d}" % (move, journal_id, period_id),
'type': 'ir.actions.act_window',
'search_view_id': res_id
}
@@ -33,7 +33,6 @@
<record id="action_idea_post_vote_values" model="ir.values">
<field name="model_id" ref="model_account_period" />
<field name="object" eval="1" />
<field name="name">Close a Period</field>
<field name="key2">client_action_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_account_period_close'))"/>
@@ -33,7 +33,6 @@
</record>
<record model="ir.values" id="action_partner_reconcile_actino">
<field name="model_id" ref="account.model_account_move_line" />
<field name="object" eval="1" />
<field name="name">Partner reconciliation</field>
<field name="key2">client_action_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_account_partner_reconcile'))" />
@@ -37,7 +37,6 @@
<record model="ir.values" id="action_account_move_line_reconcile_prompt_values">
<field name="model_id" ref="account.model_account_move_line" />
<field name="object" eval="1" />
<field name="name">Reconcile Entries</field>
<field name="key2">client_action_multi</field>
<field name="value" eval="'ir.actions.act_window,' +str(ref('action_view_account_move_line_reconcile'))" />
@@ -45,7 +45,6 @@
<record model="ir.values" id="action_account_balance_report_values1">
<field name="model_id" ref="account.model_account_account" />
<field name="object" eval="1" />
<field name="name">Trial Balance</field>
<field name="key2">client_print_multi</field>
<field name="value" eval="'ir.actions.act_window,' +str(ref('action_account_balance_menu'))" />
@@ -51,7 +51,6 @@
<record model="ir.values" id="action_account_general_ledger_values">
<field name="model_id" ref="account.model_account_account" />
<field name="object" eval="1" />
<field name="name">General Ledger</field>
<field name="key2">client_print_multi</field>
<field name="value" eval="'ir.actions.act_window,' +str(ref('action_account_general_ledger_menu'))" />
@@ -31,7 +31,6 @@
<record model="ir.values" id="account_unreconcile_values">
<field name="model_id" ref="account.model_account_move_line" />
<field name="object" eval="1" />
<field name="name">Unreconcile Entries</field>
<field name="key2">client_action_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_account_unreconcile'))" />
@@ -70,7 +69,6 @@
<record model="ir.values" id="account_unreconcile_reconcile_values">
<field name="model_id" ref="account.model_account_move_reconcile" />
<field name="object" eval="1" />
<field name="name">Unreconcile Entries</field>
<field name="key2">client_action_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_account_unreconcile_reconcile'))" />
+3 -54
View File
@@ -47,67 +47,16 @@ class account_use_model(osv.osv_memory):
def create_entries(self, cr, uid, ids, context=None):
account_model_obj = self.pool.get('account.model')
account_period_obj = self.pool.get('account.period')
account_move_obj = self.pool.get('account.move')
account_move_line_obj = self.pool.get('account.move.line')
pt_obj = self.pool.get('account.payment.term')
mod_obj = self.pool.get('ir.model.data')
if context is None:
context = {}
move_ids = []
entry = {}
data = self.read(cr, uid, ids, context=context)[0]
record_id = context and context.get('model_line', False) or False
if record_id:
data_model = account_model_obj.browse(cr, uid, data['model'], context=context)
model_ids = data['model']
else:
data_model = account_model_obj.browse(cr, uid, context['active_ids'], context=context)
for model in data_model:
entry['name'] = model.name%{'year':time.strftime('%Y'), 'month':time.strftime('%m'), 'date':time.strftime('%d')}
period_id = account_period_obj.find(cr, uid, context=context)
if not period_id:
raise osv.except_osv(_('No period found !'), _('Unable to find a valid period !'))
period_id = period_id[0]
move_id = account_move_obj.create(cr, uid, {
'ref': entry['name'],
'period_id': period_id,
'journal_id': model.journal_id.id,
})
move_ids.append(move_id)
for line in model.lines_id:
analytic_account_id = False
if line.analytic_account_id:
if not model.journal_id.analytic_journal_id:
raise osv.except_osv(_('No Analytic Journal !'),_("You have to define an analytic journal on the '%s' journal!") % (model.journal_id.name,))
analytic_account_id = line.analytic_account_id.id
val = {
'move_id': move_id,
'journal_id': model.journal_id.id,
'period_id': period_id,
'analytic_account_id': analytic_account_id
}
date_maturity = time.strftime('%Y-%m-%d')
if line.date_maturity == 'partner' and line.partner_id and line.partner_id.property_payment_term:
payment_term_id = line.partner_id.property_payment_term.id
pterm_list = pt_obj.compute(cr, uid, payment_term_id, value=1, date_ref=date_maturity)
if pterm_list:
pterm_list = [l[0] for l in pterm_list]
pterm_list.sort()
date_maturity = pterm_list[-1]
val.update({
'name': line.name,
'quantity': line.quantity,
'debit': line.debit,
'credit': line.credit,
'account_id': line.account_id.id,
'move_id': move_id,
'partner_id': line.partner_id.id,
'date': time.strftime('%Y-%m-%d'),
'date_maturity': date_maturity
})
c = context.copy()
c.update({'journal_id': model.journal_id.id,'period_id': period_id})
account_move_line_obj.create(cr, uid, val, context=c)
model_ids = context['active_ids']
move_ids = account_model_obj.generate(cr, uid, model_ids, context=context)
context.update({'move_ids':move_ids})
model_data_ids = mod_obj.search(cr, uid,[('model','=','ir.ui.view'),('name','=','view_move_form')], context=context)
@@ -73,7 +73,6 @@
<record model="ir.values" id="validate_account_move_line_values">
<field name="model_id" ref="account.model_account_move_line" />
<field name="object" eval="1" />
<field name="name">Post Journal Entries</field>
<field name="key2">client_action_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_validate_account_move_line'))" />
@@ -61,7 +61,7 @@ class account_analytic_account(osv.osv):
res[id][f] = [int(id * 1000000 + int(x[0])) for x in result]
elif f == 'last_worked_invoiced_date':
for id in ids:
res[id][f] = 0.0
res[id][f] = False
if parent_ids:
cr.execute("SELECT account_analytic_line.account_id, MAX(date) \
FROM account_analytic_line \
@@ -74,8 +74,8 @@ class account_analytic_account(osv.osv):
res[account_id][f] = sum
for account in accounts:
for child in account.child_ids:
if res[account.id].get(f, '') < res.get(child.id, {}).get(f, ''):
res[account.id][f] = res.get(child.id, {}).get(f, '')
if res[account.id][f] < res.get(child.id, {}).get(f):
res[account.id][f] = res.get(child.id, {}).get(f, False)
elif f == 'ca_to_invoice':
for id in ids:
res[id][f] = 0.0
@@ -123,7 +123,7 @@ class account_analytic_account(osv.osv):
res[id][f] = round(res.get(id, {}).get(f, 0.0), dp) + round(res2.get(id, 0.0), 2)
elif f == 'last_invoice_date':
for id in ids:
res[id][f] = ''
res[id][f] = False
if parent_ids:
cr.execute ("SELECT account_analytic_line.account_id, \
DATE(MAX(account_invoice.date_invoice)) \
@@ -137,11 +137,11 @@ class account_analytic_account(osv.osv):
res[account_id][f] = lid
for account in accounts:
for child in account.child_ids:
if res[account.id][f] < res.get(child.id, {}).get(f, ''):
res[account.id][f] = res.get(child.id, {}).get(f, '')
if res[account.id][f] < res.get(child.id, {}).get(f):
res[account.id][f] = res.get(child.id, {}).get(f, False)
elif f == 'last_worked_date':
for id in ids:
res[id][f] = ''
res[id][f] = False
if parent_ids:
cr.execute("SELECT account_analytic_line.account_id, MAX(date) \
FROM account_analytic_line \
@@ -154,8 +154,8 @@ class account_analytic_account(osv.osv):
res[account_id][f] = lwd
for account in accounts:
for child in account.child_ids:
if res[account.id][f] < res.get(child.id, {}).get(f, ''):
res[account.id][f] = res.get(child.id, {}).get(f, '')
if res[account.id][f] < res.get(child.id, {}).get(f):
res[account.id][f] = res.get(child.id, {}).get(f, False)
elif f == 'hours_qtt_non_invoiced':
for id in ids:
res[id][f] = 0.0
@@ -376,13 +376,11 @@ class account_analytic_account(osv.osv):
def _is_overdue_quantity(self, cr, uid, ids, fieldnames, args, context=None):
result = dict.fromkeys(ids, 0)
for record in self.browse(cr, uid, ids, context=context):
if record.quantity == 0.0 and record.quantity_max == 0.0:
result[record.id] = 0
else:
if record.quantity_max > 0.0:
result[record.id] = int(record.quantity >= record.quantity_max)
else:
result[record.id] = 0
return result
def _get_analytic_account(self, cr, uid, ids, context=None):
@@ -1,7 +1,6 @@
<openerp>
<data>
<menuitem icon="terp-project" id="base.menu_main_pm" name="Project" sequence="10"/>
<menuitem id="menu_invoicing" name="Billing" parent="base.menu_main_pm" sequence="4" groups="base.group_extended"/>
<menuitem id="base.menu_invoiced" name="Invoicing" parent="base.menu_base_partner" sequence="5"/>
<record id="action_hr_tree_invoiced_all" model="ir.actions.act_window">
<field name="name">All Uninvoiced Entries</field>
@@ -11,16 +10,7 @@
<field name="domain">[('invoice_id','=',False)]</field>
<field name="search_view_id" ref="account.view_account_analytic_line_filter"/>
</record>
<menuitem action="action_hr_tree_invoiced_all" id="menu_action_hr_tree_invoiced_all" parent="menu_invoicing" groups="analytic.group_analytic_accounting"/>
<record id="action_account_analytic_managed_overpassed" model="ir.actions.act_window">
<field name="name">Overpassed Accounts</field>
<field name="res_model">account.analytic.account</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,graph</field>
<field name="domain">[('date','&lt;=',time.strftime('%Y-%m-%d')),('state','=','open')]</field>
</record>
<menuitem action="action_account_analytic_managed_overpassed" id="menu_action_account_analytic_managed_overpassed" sequence="50" parent="menu_invoicing" groups="base.group_extended"/>
<menuitem action="action_hr_tree_invoiced_all" id="menu_action_hr_tree_invoiced_all" parent="base.menu_invoiced"/>
<record id="view_account_analytic_account_overdue_search" model="ir.ui.view">
<field name="name">account.analytic.account.search</field>
@@ -29,11 +19,10 @@
<field name="arch" type="xml">
<search string="Analytic Account">
<group col="8" colspan="4">
<filter icon="terp-check" name="draft" string="Draft" domain="[('state','=','draft')]" help="Contracts not signed yet"/>
<filter icon="terp-camera_test" name="open" string="Open" domain="[('state','=','open')]" help="Contracts in progress"/>
<filter icon="terp-camera_test" name="open" string="Open" domain="[('state','in',('open','draft'))]" help="Contracts in progress"/>
<filter icon="terp-gtk-media-pause" name="pending" string="Pending" domain="[('state','=','pending')]" help="Pending contracts to renew with your customer"/>
<separator orientation="vertical"/>
<filter icon="terp-go-today" string="To Renew" domain="['|', '&amp;', ('date', '!=', False), ('date', '&lt;', time.strftime('%%Y-%%m-%%d')), ('is_overdue_quantity', '=', True)]"
<filter icon="terp-go-today" string="To Renew" domain="['|', '&amp;', ('date', '!=', False), ('date', '&lt;=', time.strftime('%%Y-%%m-%%d')), ('is_overdue_quantity', '=', True)]"
name="renew"
help="The contracts to be renewed because the deadline is passed or the working hours are higher than the allocated hours" />
<filter icon="terp-go-month"
@@ -45,8 +34,7 @@
<field name="name" select="1"/>
<field name="code" select="1"/>
<field name="partner_id" select="1">
<filter string="Has Partner" name="has_partner" domain="[('partner_id', '!=', False)]" icon="terp-partner" />
<filter string="Has No Partner" name="has_no_partner" domain="[('partner_id', '=', False)]" icon="terp-partner" />
<filter string="A contract in OpenERP is an analytic account having a partner set on it." name="has_partner" domain="[('partner_id', '!=', False)]" icon="terp-partner" />
</field>
<field name="user_id">
<filter string="My Accounts" domain="[('user_id','=',uid)]" icon="terp-personal" name="my_accounts" />
@@ -71,7 +59,7 @@
<record id="action_account_analytic_overdue" model="ir.actions.act_window">
<field name="name">Overdue Accounts</field>
<field name="name">Contracts to Renew</field>
<field name="res_model">account.analytic.account</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,graph</field>
@@ -80,7 +68,34 @@
<field name="search_view_id" ref="view_account_analytic_account_overdue_search"/>
<field name="help">You will find here the contracts to be renewed because the deadline is passed or the working hours are higher than the allocated hours. OpenERP automatically sets these analytic accounts to the pending state, in order to raise a warning during the timesheets recording. Salesmen should review all pending accounts and reopen or close the according to the negotiation with the customer.</field>
</record>
<menuitem action="action_account_analytic_overdue" id="menu_action_account_analytic_overdue" sequence="50" parent="sale.menu_invoiced" groups="base.group_extended"/>
<menuitem action="action_account_analytic_overdue" id="menu_action_account_analytic_overdue" sequence="50" parent="base.menu_invoiced"/>
<record id="action_account_analytic_overdue" model="ir.actions.act_window">
<field name="name">Contracts to Renew</field>
<field name="res_model">account.analytic.account</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,graph</field>
<field name="context">{'search_default_has_partner':1, 'search_default_my_accounts':1, 'search_default_draft':1, 'search_default_pending':1, 'search_default_open':1, 'search_default_renew':1}</field>
<field name="domain">[('type','=','normal')]</field>
<field name="search_view_id" ref="view_account_analytic_account_overdue_search"/>
<field name="help">You will find here the contracts to be renewed because the deadline is passed or the working hours are higher than the allocated hours. OpenERP automatically sets these analytic accounts to the pending state, in order to raise a warning during the timesheets recording. Salesmen should review all pending accounts and reopen or close the according to the negotiation with the customer.</field>
</record>
<menuitem action="action_account_analytic_overdue" id="menu_action_account_analytic_overdue" sequence="50" parent="base.menu_invoiced"/>
<record id="action_account_analytic_overdue_all" model="ir.actions.act_window">
<field name="name">Contracts</field>
<field name="res_model">account.analytic.account</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,graph</field>
<field name="context">{'search_default_has_partner':1, 'search_default_my_accounts':1, 'search_default_draft':1, 'search_default_pending':1, 'search_default_open':1}</field>
<field name="domain">[('type','=','normal')]</field>
<field name="search_view_id" ref="view_account_analytic_account_overdue_search"/>
</record>
<menuitem id="base.menu_sales" name="Sales"
parent="base.menu_base_partner"
sequence="1"/>
<menuitem action="action_account_analytic_overdue_all" id="menu_action_account_analytic_overdue_all" sequence="50" parent="base.menu_sales"/>
</data>
</openerp>
@@ -8,6 +8,27 @@
Remove information on Account data => because they move on analysis sheet
create a page with invoicing informations
-->
<record id="account_analytic_account_invoice_stat_form" model="ir.ui.view">
<field name="name">account.analytic.account.invoice.stat.form.inherit</field>
<field name="model">account.analytic.account</field>
<field name="type">form</field>
<field name="inherit_id" ref="hr_timesheet_invoice.account_analytic_account_form_form"/>
<field eval="18" name="priority"/>
<field name="arch" type="xml">
<group name="invoice_stats" position="inside">
<field name="hours_qtt_non_invoiced"/>
<field name="ca_to_invoice"/>
<label string="" colspan="1"/>
<button
name="%(hr_timesheet_invoice.action_hr_timesheet_invoice_create_final)d"
string="Create Invoice"
type="action"
attrs="{'readonly':[('ca_to_invoice','=',0.0)]}"
icon="gtk-go-forward"/>
</group>
</field>
</record>
<record id="hr_timesheet.account_analytic_account_form_form" model="ir.ui.view">
<field name="name">account.analytic.account.invoice.form.inherit</field>
<field name="model">account.analytic.account</field>
@@ -16,7 +37,7 @@
<field eval="18" name="priority"/>
<field name="arch" type="xml">
<notebook position="inside">
<page string="Analysis summary">
<page string="Invoice Analysis">
<separator colspan="4" string="Work done stats"/>
<field name="total_cost"/>
@@ -39,10 +60,6 @@
<field name="last_invoice_date"/>
<field name="last_worked_invoiced_date"/>
<field name="last_worked_date"/>
<separator colspan="4" string="To be invoiced"/>
<field name="hours_qtt_non_invoiced"/>
<field name="ca_to_invoice"/>
</page>
<page string="Stats by month">
<field colspan="4" name="month_ids" nolabel="1">
@@ -36,7 +36,6 @@
<record model="ir.values" id="account_crossovered_analytic_values">
<field name="model_id" ref="analytic.model_account_analytic_account" />
<field name="object" eval="1" />
<field name="name">Crossovered Analytic</field>
<field name="key2">client_print_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_account_crossovered_analytic'))" />
@@ -45,4 +44,4 @@
</record>
</data>
</openerp>
</openerp>
+10 -10
View File
@@ -26,15 +26,15 @@ class purchase_order(osv.osv):
_inherit = "purchase.order"
_description = "Purchase Order"
def inv_line_create(self, cr, uid, a, ol):
line = super(purchase_order, self).inv_line_create(cr, uid, a, ol)
if ol.product_id and not ol.product_id.type == 'service':
oa = ol.product_id.property_stock_account_input and ol.product_id.property_stock_account_input.id
if not oa:
oa = ol.product_id.categ_id.property_stock_account_input_categ and ol.product_id.categ_id.property_stock_account_input_categ.id
if oa:
fpos = ol.order_id.fiscal_position or False
a = self.pool.get('account.fiscal.position').map_account(cr, uid, fpos, oa)
line[2].update({'account_id': a})
def _prepare_inv_line(self, cr, uid, account_id, order_line, context=None):
line = super(purchase_order, self)._prepare_inv_line(cr, uid, account_id, order_line, context=context)
if order_line.product_id and not order_line.product_id.type == 'service':
acc_id = order_line.product_id.property_stock_account_input and order_line.product_id.property_stock_account_input.id
if not acc_id:
acc_id = order_line.product_id.categ_id.property_stock_account_input_categ and order_line.product_id.categ_id.property_stock_account_input_categ.id
if acc_id:
fpos = order_line.order_id.fiscal_position or False
new_account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, fpos, acc_id)
line.update({'account_id': new_account_id})
return line
purchase_order()
@@ -47,11 +47,11 @@
<filter string="Year" icon="terp-go-year" name="year"
domain="[('purchase_date','&lt;=', time.strftime('%%Y-%%m-%%d')),('purchase_date','&gt;=',time.strftime('%%Y-01-01'))]"
help="Assets purchased in current year"/>
<separator orientation="vertical"/>
<filter string="This Month" icon="terp-go-month" name="this_month"
<separator orientation="vertical"/>
<filter string="Month" icon="terp-go-month" name="this_month"
domain="[('purchase_date','&lt;=',(datetime.date.today()+relativedelta(day=31)).strftime('%%Y-%%m-%%d')),('purchase_date','&gt;=',(datetime.date.today()-relativedelta(day=1)).strftime('%%Y-%%m-%%d'))]"
help="Assets purchased in current month"/>
<filter icon="terp-go-month" string="Previous Month" separator="1"
<filter icon="terp-go-month" string="Month-1" separator="1"
domain="[('purchase_date','&lt;=', (datetime.date.today() - relativedelta(day=31, months=1)).strftime('%%Y-%%m-%%d')),('purchase_date','&gt;=',(datetime.date.today() - relativedelta(day=1,months=1)).strftime('%%Y-%%m-%%d'))]"
help="Assets purchased in last month"/>
<separator orientation="vertical"/>
@@ -32,7 +32,6 @@
<record model="ir.values" id="account_budget_analytic_values">
<field name="model_id" ref="analytic.model_account_analytic_account" />
<field name="object" eval="1" />
<field name="name">Print Budgets</field>
<field name="key2">client_print_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_account_budget_analytic'))" />
@@ -31,7 +31,6 @@
</record>
<record model="ir.values" id="account_budget_crossvered_report_values">
<field name="model_id" ref="account_budget.model_crossovered_budget" />
<field name="object" eval="1" />
<field name="name">Print Budgets</field>
<field name="key2">client_print_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_account_budget_crossvered_report'))" />
@@ -32,7 +32,6 @@
<record model="ir.values" id="account_budget_crossvered_summary_report_values">
<field name="model_id" ref="account_budget.model_crossovered_budget" />
<field name="object" eval="1" />
<field name="name">Print Summary</field>
<field name="key2">client_print_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_account_budget_crossvered_summary_report'))" />
@@ -32,7 +32,6 @@
<record model="ir.values" id="account_budget_report_values">
<field name="model_id" ref="account_budget.model_account_budget_post" />
<field name="object" eval="1" />
<field name="name">Budgets</field>
<field name="key2">client_print_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_account_budget_report'))" />
@@ -32,7 +32,6 @@
<record model="ir.values" id="account_invoice_special_msg_values">
<field name="model_id" ref="account.model_account_invoice" />
<field name="object" eval="1" />
<field name="name">Account Invioce Special Message</field>
<field name="key2">client_print_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_account_invoice_special_msg'))" />
+1 -3
View File
@@ -374,7 +374,7 @@ class payment_line(osv.osv):
data = {}
move_line_obj = self.pool.get('account.move.line')
data['amount_currency'] = data['communication'] = data['partner_id'] = data['reference'] = data['date_created'] = data['bank_id'] = data['amount'] = False
data['amount_currency'] = data['communication'] = data['partner_id'] = data['bank_id'] = data['amount'] = False
if move_line_id:
line = move_line_obj.browse(cr, uid, move_line_id, context=context)
@@ -396,8 +396,6 @@ class payment_line(osv.osv):
temp_dict = self.onchange_partner(cr, uid, ids, line.partner_id.id, payment_type)
data.update(temp_dict['value'])
data['reference'] = line.ref
data['date_created'] = line.date_created
data['communication'] = line.ref
if date_prefered == 'now':
@@ -251,7 +251,7 @@
<separator colspan="4" string="Transaction Information"/>
<field name="date"/>
<group colspan="2">
<field name="amount_currency" on_change="onchange_amount(amount_currency,currency,comapny_currency)"/>
<field name="amount_currency" on_change="onchange_amount(amount_currency,currency,company_currency)"/>
<field name="currency" nolabel="1"/>
</group>
<field name="partner_id" on_change="onchange_partner(partner_id, False)" select="1"/>
+8 -8
View File
@@ -235,7 +235,7 @@ class account_voucher(osv.osv):
('pay_now','Pay Directly'),
('pay_later','Pay Later or Group Funds'),
],'Payment', select=True, readonly=True, states={'draft':[('readonly',False)]}),
'tax_id':fields.many2one('account.tax', 'Tax', readonly=True, states={'draft':[('readonly',False)]}),
'tax_id': fields.many2one('account.tax', 'Tax', readonly=True, states={'draft':[('readonly',False)]}, domain=[('price_include','=', False)], help="Only for tax excluded from price"),
'pre_line':fields.boolean('Previous Payments ?', required=False),
'date_due': fields.date('Due Date', readonly=True, select=True, states={'draft':[('readonly',False)]}),
'payment_option':fields.selection([
@@ -518,7 +518,7 @@ class account_voucher(osv.osv):
elif currency_id == company_currency:
#otherwise treatments is the same but with other field names
if line.amount_residual == price:
#if the amount residual is equal the amount voucher, we assign it to that voucher
#if the amount residual is equal the amount voucher, we assign it to that voucher
#line, whatever the other voucher lines
move_line_found = line.id
break
@@ -696,8 +696,8 @@ class account_voucher(osv.osv):
Select the context to use accordingly if it needs to be multicurrency or not.
:param voucher_id: Id of the actual voucher
:return: The returned context will be the same as given in parameter if the voucher currency is the same
than the company currency, otherwise it's a copy of the parameter with an extra key 'date' containing
:return: The returned context will be the same as given in parameter if the voucher currency is the same
than the company currency, otherwise it's a copy of the parameter with an extra key 'date' containing
the date of the voucher.
:rtype: dict
"""
@@ -726,7 +726,7 @@ class account_voucher(osv.osv):
voucher_brw = self.pool.get('account.voucher').browse(cr,uid,voucher_id,context)
debit = credit = 0.0
# TODO: is there any other alternative then the voucher type ??
# ANSWER: We can have payment and receipt "In Advance".
# ANSWER: We can have payment and receipt "In Advance".
# TODO: Make this logic available.
# -for sale, purchase we have but for the payment and receipt we do not have as based on the bank/cash journal we can not know its payment or receipt
if voucher_brw.type in ('purchase', 'payment'):
@@ -769,7 +769,7 @@ class account_voucher(osv.osv):
elif voucher_brw.journal_id.sequence_id:
name = seq_obj.next_by_id(cr, uid, voucher_brw.journal_id.sequence_id.id)
else:
raise osv.except_osv(_('Error !'),
raise osv.except_osv(_('Error !'),
_('Please define a sequence on the journal !'))
if not voucher_brw.reference:
ref = name.replace('/','')
@@ -834,7 +834,7 @@ class account_voucher(osv.osv):
def voucher_move_line_create(self, cr, uid, voucher_id, line_total, move_id, company_currency, current_currency, context=None):
'''
Create one account move line, on the given account move, per voucher line where amount is not 0.0.
It returns Tuple with tot_line what is total of difference between debit and credit and
It returns Tuple with tot_line what is total of difference between debit and credit and
a list of lists with ids to be reconciled with this format (total_deb_cred,list_of_lists).
:param voucher_id: Voucher id what we are working with
@@ -905,7 +905,7 @@ class account_voucher(osv.osv):
voucher_line = move_line_obj.create(cr, uid, move_line)
rec_ids = [voucher_line, line.move_line_id.id]
if amount_residual:
if amount_residual:
# Change difference entry
exch_lines = self._get_exchange_lines(cr, uid, line, move_id, amount_residual, company_currency, current_currency, context=context)
new_id = move_line_obj.create(cr, uid, exch_lines[0],context)
+16 -28
View File
@@ -49,18 +49,6 @@
company_id: base.main_company
view_id: account.account_journal_bank_view
-
I create new partner Mark Strauss.
-
!record {model: res.partner, id: res_partner_strauss0}:
address:
- city: paris
country_id: base.fr
name: Mark Strauss
street: 1 rue Rockfeller
type: invoice
zip: '75016'
name: Mr. Mark Strauss
-
I create the first invoice on 1st January for 200 USD
-
@@ -80,7 +68,7 @@
product_id: product.product_product_pc1
uos_id: product.product_uom_unit
journal_id: account.sales_journal
partner_id: res_partner_strauss0
partner_id: base.res_partner_seagate
reference_type: none
-
I Validate invoice by clicking on Validate button
@@ -115,7 +103,7 @@
product_id: product.product_product_pc1
uos_id: product.product_uom_unit
journal_id: account.sales_journal
partner_id: res_partner_strauss0
partner_id: base.res_partner_seagate
reference_type: none
-
I Validate invoice by clicking on Validate button
@@ -139,21 +127,21 @@
!python {model: account.voucher}: |
import netsvc, time
vals = {}
res = self.onchange_partner_id(cr, uid, [], ref("res_partner_strauss0"), ref('bank_journal_USD'), 240.00, 2, ttype='receipt', date=False)
res = self.onchange_partner_id(cr, uid, [], ref("base.res_partner_seagate"), ref('bank_journal_USD'), 240.00, 2, ttype='receipt', date=False)
vals = {
'account_id': ref('account.cash'),
'amount': 240.00,
'company_id': ref('base.main_company'),
'currency_id': ref('base.USD'),
'journal_id': ref('bank_journal_USD'),
'partner_id': ref('res_partner_strauss0'),
'partner_id': ref('base.res_partner_seagate'),
'period_id': ref('account.period_3'),
'type': 'receipt',
'date': time.strftime("%Y-%m-%d"),
'payment_option': 'with_writeoff',
'writeoff_acc_id': ref('account.a_expense'),
'comment': 'Write Off',
'name': 'First payment',
'name': 'First payment: Case 1 USD/USD',
}
vals.update(self.onchange_date(cr, uid, [], time.strftime('%Y-03-01'), ref('base.USD'), 240)['value'])
if not res['value']['line_cr_ids']:
@@ -171,7 +159,7 @@
I check that writeoff amount computed is 10.0
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_strauss0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 1 USD/USD'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
assert (voucher_id.writeoff_amount == 10.0), "Writeoff amount is not 10.0"
-
@@ -179,14 +167,14 @@
-
!python {model: account.voucher}: |
import netsvc
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_strauss0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 1 USD/USD'), ('partner_id', '=', ref('base.res_partner_seagate'))])
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr)
-
I check that the move of my first voucher is valid
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_strauss0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 1 USD/USD'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
@@ -200,7 +188,7 @@
I check that my write-off is correct. 9 debit and 10 amount_currency
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_strauss0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 1 USD/USD'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
@@ -237,7 +225,7 @@
!python {model: account.voucher}: |
import netsvc, time
vals = {}
res = self.onchange_partner_id(cr, uid, [], ref("res_partner_strauss0"), ref('bank_journal_USD'), 45.00, 2, ttype='receipt', date=False)
res = self.onchange_partner_id(cr, uid, [], ref("base.res_partner_seagate"), ref('bank_journal_USD'), 45.00, 2, ttype='receipt', date=False)
vals = {
'account_id': ref('account.cash'),
'amount': 45.00,
@@ -245,14 +233,14 @@
'company_id': ref('base.main_company'),
'currency_id': ref('base.USD'),
'journal_id': ref('bank_journal_USD'),
'partner_id': ref('res_partner_strauss0'),
'partner_id': ref('base.res_partner_seagate'),
'period_id': ref('account.period_3'),
'type': 'receipt',
'date': time.strftime("%Y-%m-%d"),
'payment_option': 'with_writeoff',
'writeoff_acc_id': ref('account.a_expense'),
'comment': 'Write Off',
'name': 'Second payment',
'name': 'Second payment: Case 1',
}
vals.update(self.onchange_date(cr, uid, [], time.strftime('%Y-04-01'), ref('base.USD'), 45.0)['value'])
if not res['value']['line_cr_ids']:
@@ -270,7 +258,7 @@
I check that writeoff amount computed is 5.0
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_strauss0'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 1'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
assert (voucher_id.writeoff_amount == 5.0), "Writeoff amount is not 5.0"
-
@@ -278,14 +266,14 @@
-
!python {model: account.voucher}: |
import netsvc
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_strauss0'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 1'), ('partner_id', '=', ref('base.res_partner_seagate'))])
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr)
-
I check that the move of my second voucher is valid
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_strauss0'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 1'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
@@ -303,7 +291,7 @@
I check that my writeoff is correct. 4.75 debit and 5 amount_currency
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_strauss0'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 1'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
@@ -29,18 +29,6 @@
currency: base.USD
company_id: base.main_company
view_id: account.account_journal_bank_view
-
I create a new partner Robert Clements
-
!record {model: res.partner, id: res_partner_clements0}:
address:
- city: marseille
country_id: base.fr
name: Robert
street: 1 rue Rockfeller
type: invoice
zip: '13016'
name: Mr.Robert Clements
-
I create the first invoice on 1st January for 200 USD
-
@@ -61,7 +49,7 @@
product_id: product.product_product_pc1
uos_id: product.product_uom_unit
journal_id: account.sales_journal
partner_id: res_partner_clements0
partner_id: base.res_partner_seagate
reference_type: none
check_total : 200
-
@@ -98,7 +86,7 @@
product_id: product.product_product_pc1
uos_id: product.product_uom_unit
journal_id: account.sales_journal
partner_id: res_partner_clements0
partner_id: base.res_partner_seagate
reference_type: none
check_total : 100.0
-
@@ -122,21 +110,21 @@
!python {model: account.voucher}: |
import netsvc, time
vals = {}
res = self.onchange_partner_id(cr, uid, [], ref("res_partner_clements0"), ref('bank_journal_EUR'), 240.0, 2, ttype='payment', date=False)
res = self.onchange_partner_id(cr, uid, [], ref("base.res_partner_seagate"), ref('bank_journal_EUR'), 240.0, 2, ttype='payment', date=False)
vals = {
'account_id': ref('account.cash'),
'amount': 240.0,
'company_id': ref('base.main_company'),
'currency_id': ref('base.EUR'),
'journal_id': ref('bank_journal_EUR'),
'partner_id': ref('res_partner_clements0'),
'partner_id': ref('base.res_partner_seagate'),
'period_id': ref('account.period_3'),
'type': 'payment',
'date': time.strftime("%Y-03-01"),
'payment_option': 'with_writeoff',
'writeoff_acc_id': ref('account.a_expense'),
'comment': 'Write Off',
'name': 'First payment',
'name': 'First payment: Case 2 SUPPL USD/EUR',
}
if not res['value']['line_dr_ids']:
res['value']['line_dr_ids'] = [{'type': 'dr', 'account_id': ref('account.a_pay'),}]
@@ -153,14 +141,14 @@
I check that writeoff amount computed is -15.0
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_clements0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
assert (voucher_id.writeoff_amount == -15.0), "Writeoff amount is not -15.0"
-
I check that currency rate difference is 34.0
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_clements0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
assert (voucher_id.currency_rate_difference == 34.0), "Currency rate difference is not 34.0"
-
@@ -168,14 +156,14 @@
-
!python {model: account.voucher}: |
import netsvc
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_clements0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_seagate'))])
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr)
-
I check that the move of my voucher is valid
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_clements0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
@@ -191,7 +179,7 @@
I check that my writeoff is correct. -15 in credit with no amount_currency
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_clements0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
@@ -229,21 +217,21 @@
!python {model: account.voucher}: |
import netsvc, time
vals = {}
res = self.onchange_partner_id(cr, uid, [], ref("res_partner_clements0"), ref('bank_journal_EUR'), 45.0, 2, ttype='payment', date=False)
res = self.onchange_partner_id(cr, uid, [], ref("base.res_partner_seagate"), ref('bank_journal_EUR'), 45.0, 2, ttype='payment', date=False)
vals = {
'account_id': ref('account.cash'),
'amount': 45.0,
'company_id': ref('base.main_company'),
'currency_id': ref('base.USD'),
'journal_id': ref('bank_journal_USD'),
'partner_id': ref('res_partner_clements0'),
'partner_id': ref('base.res_partner_seagate'),
'period_id': ref('account.period_3'),
'type': 'payment',
'date': time.strftime("%Y-%m-%d"),
'payment_option': 'with_writeoff',
'writeoff_acc_id': ref('account.a_expense'),
'comment': 'Write Off',
'name': 'Second payment',
'name': 'Second payment: Case 2 SUPPL USD/EUR',
}
vals.update(self.onchange_date(cr, uid, [], time.strftime('%Y-04-01'), ref('base.USD'), 45.0)['value'])
if not res['value']['line_dr_ids']:
@@ -261,14 +249,14 @@
I check that writeoff amount computed is -5.0
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_clements0'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
assert (voucher_id.writeoff_amount == 5.0), "Writeoff amount is not 5.0"
-
I check that currency rate difference is 8.50
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_clements0'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
assert (voucher_id.currency_rate_difference == 8.50), "Currency rate difference is not 8.50"
-
@@ -276,14 +264,14 @@
-
!python {model: account.voucher}: |
import netsvc
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_clements0'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_seagate'))])
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr)
-
I check that my voucher state is posted
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_clements0'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
assert voucher_id.state == 'posted', "Voucher state is not posted"
-
@@ -296,7 +284,7 @@
I check that my writeoff is correct. 4.75 in credit and 5 in amount_currency
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_clements0'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
@@ -66,18 +66,6 @@
currency: base.USD
company_id: base.main_company
view_id: account.account_journal_bank_view
-
I create a new partner Michael Geller
-
!record {model: res.partner, id: res_partner_michael0}:
address:
- city: paris
country_id: base.fr
name: Michael
street: 1 rue Rockfeller
type: invoice
zip: '75016'
name: Mr.Michael Geller
-
I create the first invoice on 1st January for 200 USD
-
@@ -97,7 +85,7 @@
product_id: product.product_product_pc1
uos_id: product.product_uom_unit
journal_id: account.sales_journal
partner_id: res_partner_michael0
partner_id: base.res_partner_seagate
reference_type: none
-
I Validate invoice by clicking on Validate button
@@ -132,7 +120,7 @@
product_id: product.product_product_pc1
uos_id: product.product_uom_unit
journal_id: account.sales_journal
partner_id: res_partner_michael0
partner_id: base.res_partner_seagate
reference_type: none
-
I Validate invoice by clicking on Validate button
@@ -150,26 +138,26 @@
assert (move_line.debit - move_line.credit == 80), "Invoice move is incorrect for debtors account"
-
I create the first voucher of payment
<create with values 200 EUR (€), journal EUR, and fills amount 130€ for the invoice of 200$ and 70€ for the invoice of 100$>
<create with values 200 EUR, journal EUR, and fills amount 130 EUR for the invoice of 200 USD and 70 EUR for the invoice of 100 USD>
-
!python {model: account.voucher}: |
import netsvc, time
vals = {}
res = self.onchange_partner_id(cr, uid, [], ref("res_partner_michael0"), ref('bank_journal_EUR'), 240.0, False, ttype='receipt', date=False)
res = self.onchange_partner_id(cr, uid, [], ref("base.res_partner_seagate"), ref('bank_journal_EUR'), 240.0, False, ttype='receipt', date=False)
vals = {
'account_id': ref('account.cash'),
'amount': 200.0,
'company_id': ref('base.main_company'),
'currency_id': False,
'journal_id': ref('bank_journal_EUR'),
'partner_id': ref('res_partner_michael0'),
'partner_id': ref('base.res_partner_seagate'),
'period_id': ref('account.period_3'),
'type': 'receipt',
'date': time.strftime("%Y-03-01"),
'payment_option': 'with_writeoff',
'writeoff_acc_id': ref('account.a_expense'),
'comment': 'Write Off',
'name': 'First payment',
'name': 'First payment: Case 2 USD/EUR DR EUR',
}
if not res['value']['line_cr_ids']:
res['value']['line_cr_ids'] = [{'type': 'cr', 'account_id': ref('account.a_recv'),}]
@@ -187,14 +175,14 @@
-
!python {model: account.voucher}: |
import netsvc
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_michael0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 USD/EUR DR EUR'), ('partner_id', '=', ref('base.res_partner_seagate'))])
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr)
-
I check that the move of my voucher is valid
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_michael0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 USD/EUR DR EUR'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
@@ -206,7 +194,7 @@
I check that the debtor account has 2 new lines with 0 in amount_currency columns and their credit columns are 130 and 70
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_michael0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 USD/EUR DR EUR'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
@@ -220,7 +208,6 @@
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
print move_line.amount_residual, move_line.amount_residual_currency
assert (move_line.amount_residual_currency == 55.56 and move_line.amount_residual == 20) , "Residual amount is not correct for first Invoice"
-
I check the residual amuont of Invoice2, should be 22.22 in residual currency and 10 in amount_residual
@@ -238,14 +225,14 @@
!python {model: account.voucher}: |
import netsvc, time
vals = {}
res = self.onchange_partner_id(cr, uid, [], ref("res_partner_michael0"), ref('bank_journal_USD'), 80.0, 2, ttype='receipt', date=False)
res = self.onchange_partner_id(cr, uid, [], ref("base.res_partner_seagate"), ref('bank_journal_USD'), 80.0, 2, ttype='receipt', date=False)
vals = {
'account_id': ref('account.cash'),
'amount': 80.0,
'company_id': ref('base.main_company'),
'currency_id': ref('base.USD'),
'journal_id': ref('bank_journal_USD'),
'partner_id': ref('res_partner_michael0'),
'partner_id': ref('base.res_partner_seagate'),
'period_id': ref('account.period_3'),
'type': 'receipt',
'date': time.strftime("%Y-%m-%d"),
@@ -253,7 +240,7 @@
'writeoff_acc_id': ref('account.a_expense'),
'exchange_acc_id': ref('account.o_expense'),
'comment': 'Write Off',
'name': 'Second payment',
'name': 'Second payment: Case 2 SUPPL USD/EUR DR EUR',
}
vals.update(self.onchange_date(cr, uid, [], time.strftime('%Y-04-01'), ref('base.USD'), 80.0)['value'])
if not res['value']['line_cr_ids']:
@@ -271,7 +258,7 @@
I check that writeoff amount computed is 2.22
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_michael0'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR DR EUR'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
assert (round(voucher_id.writeoff_amount, 2) == 2.22), "Writeoff amount is not 2.22$"
-
@@ -279,14 +266,14 @@
-
!python {model: account.voucher}: |
import netsvc
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_michael0'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR DR EUR'), ('partner_id', '=', ref('base.res_partner_seagate'))])
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr)
-
I check that my voucher state is posted
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_michael0'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR DR EUR'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
assert voucher_id.state == 'posted', "Voucher state is not posted"
-
@@ -299,7 +286,7 @@
I check that my writeoff is correct. 2.11 in credit and 2.22 in amount_currency
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_michael0'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR DR EUR'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
@@ -66,18 +66,6 @@
currency: base.USD
company_id: base.main_company
view_id: account.account_journal_bank_view
-
I create a new partner Michal Gallette
-
!record {model: res.partner, id: res_partner_michal1}:
address:
- city: paris
country_id: base.fr
name: Michal
street: 1 rue Rockfeller
type: invoice
zip: '75016'
name: Mr.Michal Gallette
-
I create the first invoice on 1st January for 200 USD
-
@@ -97,7 +85,7 @@
product_id: product.product_product_pc1
uos_id: product.product_uom_unit
journal_id: account.sales_journal
partner_id: res_partner_michal1
partner_id: base.res_partner_seagate
reference_type: none
-
I Validate invoice by clicking on Validate button
@@ -132,7 +120,7 @@
product_id: product.product_product_pc1
uos_id: product.product_uom_unit
journal_id: account.sales_journal
partner_id: res_partner_michal1
partner_id: base.res_partner_seagate
reference_type: none
-
I Validate invoice by clicking on Validate button
@@ -155,21 +143,21 @@
!python {model: account.voucher}: |
import netsvc, time
vals = {}
res = self.onchange_partner_id(cr, uid, [], ref("res_partner_michal1"), ref('bank_journal_EUR'), 200.0, False, ttype='receipt', date=False)
res = self.onchange_partner_id(cr, uid, [], ref("base.res_partner_seagate"), ref('bank_journal_EUR'), 200.0, False, ttype='receipt', date=False)
vals = {
'account_id': ref('account.cash'),
'amount': 200.0,
'company_id': ref('base.main_company'),
'currency_id': ref('base.EUR'),
'journal_id': ref('bank_journal_EUR'),
'partner_id': ref('res_partner_michal1'),
'partner_id': ref('base.res_partner_seagate'),
'period_id': ref('account.period_3'),
'type': 'receipt',
'date': time.strftime("%Y-03-01"),
'payment_option': 'with_writeoff',
'writeoff_acc_id': ref('account.a_expense'),
'comment': 'Write Off',
'name': 'First payment',
'name': 'First payment: Case 2 USD/EUR DR USD',
}
if not res['value']['line_cr_ids']:
res['value']['line_cr_ids'] = [{'type': 'cr', 'account_id': ref('account.a_recv'),}]
@@ -187,14 +175,14 @@
-
!python {model: account.voucher}: |
import netsvc
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_michal1'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 USD/EUR DR USD'), ('partner_id', '=', ref('base.res_partner_seagate'))])
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr)
-
I check that the move of my voucher is valid
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_michal1'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 USD/EUR DR USD'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
@@ -206,7 +194,7 @@
I check that the debtor account has 2 new lines with 144.44 and 77.78 in amount_currency columns and their credit columns are 130 and 70
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_michal1'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 USD/EUR DR USD'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
@@ -240,14 +228,14 @@
!python {model: account.voucher}: |
import netsvc, time
vals = {}
res = self.onchange_partner_id(cr, uid, [], ref("res_partner_michal1"), ref('bank_journal_USD'), 80.0, ref('base.USD'), ttype='receipt', date=False)
res = self.onchange_partner_id(cr, uid, [], ref("base.res_partner_seagate"), ref('bank_journal_USD'), 80.0, ref('base.USD'), ttype='receipt', date=False)
vals = {
'account_id': ref('account.cash'),
'amount': 80.0,
'company_id': ref('base.main_company'),
'currency_id': ref('base.USD'),
'journal_id': ref('bank_journal_USD'),
'partner_id': ref('res_partner_michal1'),
'partner_id': ref('base.res_partner_seagate'),
'period_id': ref('account.period_3'),
'type': 'receipt',
'date': time.strftime("%Y-%m-%d"),
@@ -255,7 +243,7 @@
'writeoff_acc_id': ref('account.a_expense'),
'exchange_acc_id': ref('account.o_expense'),
'comment': 'Write Off',
'name': 'Second payment',
'name': 'Second payment: Case 2 SUPPL USD/EUR DR USD',
}
vals.update(self.onchange_date(cr, uid, [], time.strftime('%Y-04-01'), ref('base.USD'), 80.0)['value'])
if not res['value']['line_cr_ids']:
@@ -273,7 +261,7 @@
I check that writeoff amount computed is 2.22
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_michal1'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR DR USD'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
assert (round(voucher_id.writeoff_amount, 2) == 2.22), "Writeoff amount is not 2.22$"
-
@@ -281,14 +269,14 @@
-
!python {model: account.voucher}: |
import netsvc
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_michal1'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR DR USD'), ('partner_id', '=', ref('base.res_partner_seagate'))])
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr)
-
I check that my voucher state is posted
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_michal1'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR DR USD'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
assert voucher_id.state == 'posted', "Voucher state is not posted"
-
@@ -301,7 +289,7 @@
I check that my writeoff is correct. 2.11 in credit and 2.22 in amount_currency
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_michal1'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR DR USD'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
+16 -28
View File
@@ -29,18 +29,6 @@
currency: base.EUR
company_id: base.main_company
view_id: account.account_journal_bank_view
-
I create a new partner Peter Lawson.
-
!record {model: res.partner, id: res_partner_peter0}:
address:
- city: paris
country_id: base.fr
name: Peter
street: 1 rue Rockfeller
type: invoice
zip: '75016'
name: Mr.Peter Lawson
-
I create the first invoice on 1st January for 150 EUR
-
@@ -60,7 +48,7 @@
product_id: product.product_product_pc1
uos_id: product.product_uom_unit
journal_id: account.sales_journal
partner_id: res_partner_peter0
partner_id: base.res_partner_seagate
reference_type: none
-
I Validate invoice by clicking on Validate button
@@ -95,7 +83,7 @@
product_id: product.product_product_pc1
uos_id: product.product_uom_unit
journal_id: account.sales_journal
partner_id: res_partner_peter0
partner_id: base.res_partner_seagate
reference_type: none
-
I Validate invoice by clicking on Validate button
@@ -118,21 +106,21 @@
!python {model: account.voucher}: |
import netsvc, time
vals = {}
res = self.onchange_partner_id(cr, uid, [], ref("res_partner_peter0"), ref('bank_journal_EUR'), 120.00, False, ttype='receipt', date=False)
res = self.onchange_partner_id(cr, uid, [], ref("base.res_partner_seagate"), ref('bank_journal_EUR'), 120.00, False, ttype='receipt', date=False)
vals = {
'account_id': ref('account.cash'),
'amount': 120.00,
'company_id': ref('base.main_company'),
'currency_id': ref('base.EUR'),
'journal_id': ref('bank_journal_EUR'),
'partner_id': ref('res_partner_peter0'),
'partner_id': ref('base.res_partner_seagate'),
'period_id': ref('account.period_3'),
'type': 'receipt',
'date': time.strftime("%Y-03-01"),
'payment_option': 'with_writeoff',
'writeoff_acc_id': ref('account.a_expense'),
'comment': 'Write Off',
'name': 'First payment',
'name': 'First payment: Case 3',
}
if not res['value']['line_cr_ids']:
res['value']['line_cr_ids'] = [{'type': 'cr', 'account_id': ref('account.a_recv'),}]
@@ -149,7 +137,7 @@
I check that writeoff amount computed is 0.00
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment'),('partner_id', '=', ref('res_partner_peter0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 3'),('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
assert (voucher_id.writeoff_amount == 0.00), "Writeoff amount is not 0.00"
-
@@ -157,14 +145,14 @@
-
!python {model: account.voucher}: |
import netsvc
voucher = self.search(cr, uid, [('name', '=', 'First payment'),('partner_id', '=', ref('res_partner_peter0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 3'),('partner_id', '=', ref('base.res_partner_seagate'))])
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr)
-
I check that the move of my first voucher is valid
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment'),('partner_id', '=', ref('res_partner_peter0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 3'),('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
@@ -176,7 +164,7 @@
I check that the debtor account has 2 new lines with 0.00 and 0.00 in amount_currency columns and their credit are 20 and 100 respectively
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment'),('partner_id', '=', ref('res_partner_peter0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 3'),('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
@@ -210,21 +198,21 @@
!python {model: account.voucher}: |
import netsvc, time
vals = {}
res = self.onchange_partner_id(cr, uid, [], ref("res_partner_peter0"), ref('bank_journal_EUR'), 120.00, False, ttype='receipt', date=False)
res = self.onchange_partner_id(cr, uid, [], ref("base.res_partner_seagate"), ref('bank_journal_EUR'), 120.00, False, ttype='receipt', date=False)
vals = {
'account_id': ref('account.cash'),
'amount': 120.00,
'company_id': ref('base.main_company'),
'currency_id': ref('base.EUR'),
'journal_id': ref('bank_journal_EUR'),
'partner_id': ref('res_partner_peter0'),
'partner_id': ref('base.res_partner_seagate'),
'period_id': ref('account.period_3'),
'type': 'receipt',
'date': time.strftime("%Y-04-01"),
'payment_option': 'with_writeoff',
'writeoff_acc_id': ref('account.a_expense'),
'comment': 'Write Off',
'name': 'Second payment',
'name': 'Second payment: Case 3',
}
if not res['value']['line_cr_ids']:
res['value']['line_cr_ids'] = [{'type': 'cr', 'account_id': ref('account.a_recv'),}]
@@ -241,7 +229,7 @@
I check that writeoff amount computed is 0.00
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment'),('partner_id', '=', ref('res_partner_peter0'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 3'),('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
assert (voucher_id.writeoff_amount == 0.00), "Writeoff amount is not 0"
-
@@ -249,14 +237,14 @@
-
!python {model: account.voucher}: |
import netsvc
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_peter0'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 3'), ('partner_id', '=', ref('base.res_partner_seagate'))])
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr)
-
I check that the move of my second voucher is valid
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_peter0'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 3'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
@@ -268,7 +256,7 @@
I check that the debtor account has 2 new lines with 0.00 and 0.00 in amount_currency columns and their credit are 70 and 50
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment'), ('partner_id', '=', ref('res_partner_peter0'))])
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 3'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
+8 -20
View File
@@ -43,18 +43,6 @@
currency: base.CHF
company_id: base.main_company
view_id: account.account_journal_bank_view
-
I create a new partner John Armani.
-
!record {model: res.partner, id: res_partner_john0}:
address:
- city: paris
country_id: base.fr
name: John
street: 1 rue Rockfeller
type: invoice
zip: '75016'
name: Mr.John Armani
-
I create the first invoice on 1st January for 200 CAD
-
@@ -74,7 +62,7 @@
product_id: product.product_product_pc1
uos_id: product.product_uom_unit
journal_id: account.sales_journal
partner_id: res_partner_john0
partner_id: base.res_partner_seagate
reference_type: none
-
I Validate invoice by clicking on Validate button
@@ -97,14 +85,14 @@
!python {model: account.voucher}: |
import netsvc, time
vals = {}
res = self.onchange_partner_id(cr, uid, [], ref("res_partner_john0"), ref('bank_journal_CHF'), 200.00, ref('base.CHF'), ttype='receipt', date=False)
res = self.onchange_partner_id(cr, uid, [], ref("base.res_partner_seagate"), ref('bank_journal_CHF'), 200.00, ref('base.CHF'), ttype='receipt', date=False)
vals = {
'account_id': ref('account.cash'),
'amount': 200.00,
'company_id': ref('base.main_company'),
'currency_id': ref('base.CHF'),
'journal_id': ref('bank_journal_CHF'),
'partner_id': ref('res_partner_john0'),
'partner_id': ref('base.res_partner_seagate'),
'period_id': ref('account.period_3'),
'type': 'receipt',
'date': time.strftime("%Y-%m-%d"),
@@ -112,7 +100,7 @@
'writeoff_acc_id': ref('account.a_expense'),
'exchange_acc_id': ref('account.o_expense'),
'comment': 'Write Off',
'name': 'First payment',
'name': 'First payment: Case 4',
}
vals.update(self.onchange_date(cr, uid, [], time.strftime('%Y-03-01'), ref('base.CHF'), 200.0)['value'])
if not res['value']['line_cr_ids']:
@@ -128,7 +116,7 @@
I check that writeoff amount computed is 13.26
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_john0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 4'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
assert (round(voucher_id.writeoff_amount,2) == 13.26), "Writeoff amount is not 13.26 CHF"
-
@@ -136,14 +124,14 @@
-
!python {model: account.voucher}: |
import netsvc
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_john0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 4'), ('partner_id', '=', ref('base.res_partner_seagate'))])
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'account.voucher', voucher[0], 'proforma_voucher', cr)
-
I check that the move of my voucher is valid
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_john0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 4'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
@@ -160,7 +148,7 @@
I check that my writeoff is correct. 11.05 credit and 13.26 amount_currency
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment'), ('partner_id', '=', ref('res_partner_john0'))])
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 4'), ('partner_id', '=', ref('base.res_partner_seagate'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
+8 -2
View File
@@ -150,7 +150,7 @@ class account_analytic_account(osv.osv):
_columns = {
'name': fields.char('Account Name', size=128, required=True),
'complete_name': fields.function(_complete_name_calc, type='char', string='Full Account Name'),
'code': fields.char('Account Code', size=24, select=True),
'code': fields.char('Code/Reference', size=24, select=True),
'type': fields.selection([('view','View'), ('normal','Normal')], 'Account Type', help='If you select the View Type, it means you won\'t allow to create journal entries using that account.'),
'description': fields.text('Description'),
'parent_id': fields.many2one('account.analytic.account', 'Parent Analytic Account', select=2),
@@ -168,7 +168,7 @@ class account_analytic_account(osv.osv):
'date_start': fields.date('Date Start'),
'date': fields.date('Date End', select=True),
'company_id': fields.many2one('res.company', 'Company', required=False), #not required because we want to allow different companies to use the same chart of account, except for leaf accounts.
'state': fields.selection([('draft','New'),('open','Started'), ('pending','Pending'),('cancelled', 'Cancelled'),('close','Closed'),('template', 'Template')], 'State', required=True,
'state': fields.selection([('template', 'Template'),('draft','New'),('open','Open'), ('pending','Pending'),('cancelled', 'Cancelled'),('close','Closed')], 'State', required=True,
help='* When an account is created its in \'Draft\' state.\
\n* If any associated partner is there, it can be in \'Open\' state.\
\n* If any pending balance is there it can be in \'Pending\'. \
@@ -217,6 +217,12 @@ class account_analytic_account(osv.osv):
default['line_ids'] = []
return super(account_analytic_account, self).copy(cr, uid, id, default, context=context)
def on_change_partner_id(self, cr, uid, id, partner_id, context={}):
if not partner_id:
return {'value': {'contact_id': False}}
addr = self.pool.get('res.partner').address_get(cr, uid, [partner_id], ['invoice'])
return {'value': {'contact_id': addr.get('invoice', False)}}
def on_change_company(self, cr, uid, id, company_id):
if not company_id:
return {}
+17 -27
View File
@@ -7,26 +7,20 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Auction manager ">
<hpaned position="100">
<child1>
<action
string="Latest objects"
name="%(auction.action_report_latest_objects_manager_tree)d"
height="200"
width="510" />
<board style="2-1">
<column>
<action string="Latest objects"
name="%(auction.action_report_latest_objects_manager_tree)d"/>
<action string="Latest deposits"
name="%(auction.action_report_latest_doposit_manager_tree)d"/>
<action
string="Summary pointing by user"
<action string="Summary pointing by user"
name="%(auction.action_report_auction_sign_in_out_manager_tree1)d"
view_mode="tree,graph"
/>
view_mode="tree,graph"/>
<action string="Objects statistics"
name="%(auction.action_report_object_encoded_manager_tree)d"
/>
</child1>
name="%(auction.action_report_object_encoded_manager_tree)d"/>
</column>
<child2>
<column>
<button
string="My board"
name="%(open_board_auction)d"
@@ -34,22 +28,18 @@
type="action"
colspan="4"
/>
<action
string="Total Adjudications"
<action string="Total Adjudications"
name="%(auction.action_report_auction_adjudication_tree)d"
view_mode="graph,tree"
colspan="4"/>
<action
string="Min/Adj/Max"
view_mode="graph,tree"/>
<action string="Min/Adj/Max"
name="%(auction.action_report_auction_estimation_adj_category_manager_tree)d"
view_mode="graph,tree" colspan="4" />
view_mode="graph,tree"/>
<action
string="Objects by day"
<action string="Objects by day"
name="%(auction.action_report_auction_object_date_tree)d"
view_mode="graph,tree" colspan="4" />
</child2>
</hpaned>
view_mode="graph,tree"/>
</column>
</board>
</form>
</field>
</record>
+15 -23
View File
@@ -58,36 +58,28 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Auction manager ">
<hpaned position="100">
<child1>
<action
string="Latest objects"
name="%(auction.action_report_latest_objects_tree)d"
height="200"
width="510" />
<board style="2-1">
<column>
<action string="Latest objects"
name="%(auction.action_report_latest_objects_tree)d"/>
<action string="Latest deposits"
name="%(auction.action_report_latest_doposit_tree)d"/>
<action string="Objects statistics"
name="%(auction.action_report_object_encoded_tree)d"
/>
</child1>
name="%(auction.action_report_object_encoded_tree)d"/>
</column>
<child2>
<action
string="Total Adjudications"
<column>
<action string="Total Adjudications"
name="%(auction.action_report_auction_adjudication_tree)d"
view_mode="graph,tree"
colspan="4"/>
<action
string="Min/Adj/Max"
view_mode="graph,tree"/>
<action string="Min/Adj/Max"
name="%(auction.action_report_auction_lots_estimation_adj_category_tree)d"
view_mode="graph,tree" colspan="4" />
<action
string="Objects by day"
view_mode="graph,tree"/>
<action string="Objects by day"
name="%(auction.action_report_auction_object_date_tree)d"
view_mode="graph,tree" colspan="4" />
</child2>
</hpaned>
view_mode="graph,tree"/>
</column>
</board>
</form>
</field>
</record>
+1 -1
View File
@@ -27,7 +27,7 @@ def convert(from_file, to_file):
__convert(from_file, to_file, size=95)
def __convert(from_file, to_file, size=95):
import Image, ImageDraw, ImageFilter
from PIL import Image, ImageDraw, ImageFilter
im = Image.open(from_file)
if float(im.size[1]/im.size[0])>2:
im = im.resize((im.size[0]*size/im.size[1], size))
+8 -35
View File
@@ -34,21 +34,8 @@
-
!record {model: account.journal, id: account.sales_journal}:
analytic_journal_id: account.cose_journal_sale
-
I'm creating new Seller "Mr. Pinakin" with him email "info@mycustomer.com".
-
!record {model: res.partner, id: res_partner_mrpinakin0}:
address:
- city: Namur
country_id: base.be
phone: (+32).10.45.18.77
street: 23, street ways
type: default
zip: '2324324'
email: 'info@mycustomer.com'
name: Mr. Pinakin
-
-
I'm creating new Buyer "Mr. Patel" with his email "info@myinfobid.com".
-
!record {model: res.partner, id: res_partner_mrpatel0}:
@@ -61,21 +48,7 @@
email: 'info@myinfobid.com'
name: Mr. Patel
-
I'm creating new Buyer "Mr. Johnson" with his email "info@mrkjohnson.com".
-
!record {model: res.partner, id: res_partner_mrkjohnson0}:
address:
- city: paris
country_id: base.fr
name: Mark Johnson
street: 1 rue Rockfeller
type: invoice
zip: '75016'
email: 'info@mrkjohnson.com'
name: Mr. Mark Johnson
-
-
I'm creating new Buyer "Mr. Rahi" with his email "info@poalrahi.com".
-
!record {model: res.partner, id: res_partner_poalrahi0}:
@@ -140,7 +113,7 @@
date_dep: !eval "'%s-08-01' %(datetime.now().year)"
method: keep
name: AD/006
partner_id: res_partner_mrpinakin0
partner_id: base.res_partner_maxtor
specific_cost_ids:
- account: auction.auction_expense
amount: 200.0
@@ -149,7 +122,7 @@
I create a new object wooden-chair which is to be auctioned.
-
!record {model: auction.lots, id: auction_lots_woodenchair0}:
ach_uid: res_partner_mrkjohnson0
ach_uid: base.res_partner_seagate
artist_id: auction_artists_vincentvangogh0
auction_id: auction_dates_antiquefurnitureexhibition0
bord_vnd_id: auction_deposit_ad0
@@ -199,12 +172,12 @@
price: 3200.0
-
I create another bid for an object "wooden-chair" bid by a Mr.Johnson
I create another bid for an object "wooden-chair" bid by a Mr.Seagate
-
!record {model: auction.bid, id: auction_bid_bid2}:
auction_id: auction_dates_antiquefurnitureexhibition0
name: bid/003
partner_id: res_partner_mrkjohnson0
partner_id: base.res_partner_seagate
-
I create a bid line.
-
@@ -215,7 +188,7 @@
lot_id: auction.auction_lots_woodenchair0
price: 4000.0
-
Mr. MarkJohnson bid are selected as the Finalist Bid with 4000 Euro
Mr. Seagate bids are selected as the Finalist Bid with 4000 Euro
-
I check that buyer price and seller price gets bound with the value
-
@@ -250,7 +223,7 @@
-
!record {model: auction.lots.make.invoice.buyer, id: auction_lots_make_invoice_buyer_0}:
amount: 3090.0
buyer_id: res_partner_mrkjohnson0
buyer_id: base.res_partner_seagate
number: !eval "'%s/003' %(datetime.now().year)"
objects: 1
-
+26 -20
View File
@@ -147,7 +147,7 @@ the rule to mark CC(mail to any other person defined in actions)."),
res['value'] = {'filter_id':False}
return res
def pre_action(self, cr, uid, ids, model, context=None):
def post_action(self, cr, uid, ids, model, context=None):
# Searching for action rules
cr.execute("SELECT model.model, rule.id FROM base_action_rule rule \
LEFT JOIN ir_model model on (model.id = rule.model_id) \
@@ -156,7 +156,7 @@ the rule to mark CC(mail to any other person defined in actions)."),
# Check if any rule matching with current object
for obj_name, rule_id in res:
if not (model == obj_name):
continue
continue # TODO add this condition in the WHERE clause above.
else:
obj = self.pool.get(obj_name)
# If the rule doesn't involve a time condition, run it immediately
@@ -166,31 +166,36 @@ the rule to mark CC(mail to any other person defined in actions)."),
return True
def _create(self, old_create, model, context=None):
if context is None:
context = {}
def make_call_old(cr, uid, vals, context=context):
"""
Return a wrapper around `old_create` calling both `old_create` and
`post_action`, in that order.
"""
def wrapper(cr, uid, vals, context=context):
new_id = old_create(cr, uid, vals, context=context)
if not context.get('action'):
self.pre_action(cr, uid, [new_id], model, context=context)
self.post_action(cr, uid, [new_id], model, context=context)
return new_id
return make_call_old
return wrapper
def _write(self, old_write, model, context=None):
if context is None:
context = {}
def make_call_old(cr, uid, ids, vals, context=context):
if context is None:
context = {}
"""
Return a wrapper around `old_write` calling both `old_write` and
`post_action`, in that order.
"""
def wrapper(cr, uid, ids, vals, context=context):
if isinstance(ids, (str, int, long)):
ids = [ids]
old_write(cr, uid, ids, vals, context=context)
if not context.get('action'):
self.pre_action(cr, uid, ids, model, context=context)
return old_write(cr, uid, ids, vals, context=context)
return make_call_old
self.post_action(cr, uid, ids, model, context=context)
return True
return wrapper
def _register_hook(self, cr, uid, ids, context=None):
if context is None:
context = {}
"""
Wrap every `create` and `write` methods of the models specified by
the rules (given by `ids`).
"""
for action_rule in self.browse(cr, uid, ids, context=context):
model = action_rule.model_id.model
obj_pool = self.pool.get(model)
@@ -200,15 +205,16 @@ the rule to mark CC(mail to any other person defined in actions)."),
obj_pool.base_action_ruled = True
return True
def create(self, cr, uid, vals, context=None):
res_id = super(base_action_rule, self).create(cr, uid, vals, context=context)
self._register_hook(cr, uid, [res_id], context=context)
return res_id
def write(self, cr, uid, ids, vals, context=None):
res = super(base_action_rule, self).write(cr, uid, ids, vals, context=context)
super(base_action_rule, self).write(cr, uid, ids, vals, context=context)
self._register_hook(cr, uid, ids, context=context)
return res
return True
def _check(self, cr, uid, automatic=False, use_new_cursor=False, \
context=None):
+1 -4
View File
@@ -310,10 +310,7 @@ class base_setup_installer(osv.osv_memory):
cr.commit()
self.pool = pooler.restart_pool(cr.dbname, update_module=True)[1]
if 'html' in context:
return {'type' : 'ir.actions.reload'}
else:
return {'type' : 'ir.actions.act_window_close'}
return self.pool.get('res.config').next(cr, uid, [], context=context)
#Migrate data from another application Conf wiz
+94 -44
View File
@@ -7,24 +7,24 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.4\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2011-01-11 11:14+0000\n"
"PO-Revision-Date: 2011-11-07 12:48+0000\n"
"Last-Translator: OpenERP Administrators <Unknown>\n"
"PO-Revision-Date: 2011-11-14 11:20+0000\n"
"Last-Translator: kifcaliph <kifcaliph@hotmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2011-11-08 05:40+0000\n"
"X-Generator: Launchpad (build 14231)\n"
"X-Launchpad-Export-Date: 2011-11-15 05:06+0000\n"
"X-Generator: Launchpad (build 14291)\n"
#. module: base_setup
#: field:base.setup.company,city:0
msgid "City"
msgstr ""
msgstr "المدينة"
#. module: base_setup
#: view:base.setup.installer:0
msgid "Install"
msgstr ""
msgstr "تثبيت"
#. module: base_setup
#: field:base.setup.installer,account_voucher:0
@@ -34,12 +34,12 @@ msgstr "الفواتير"
#. module: base_setup
#: field:base.setup.installer,hr:0
msgid "Human Resources"
msgstr ""
msgstr "الموارد البشرية"
#. module: base_setup
#: field:base.setup.company,email:0
msgid "E-mail"
msgstr ""
msgstr "البريد الإلكتروني"
#. module: base_setup
#: field:base.setup.company,account_no:0
@@ -54,19 +54,19 @@ msgstr "أدوات إضافية"
#. module: base_setup
#: field:base.setup.company,rml_footer1:0
msgid "Report Footer 1"
msgstr ""
msgstr "تذييل تقرير 1"
#. module: base_setup
#: help:base.setup.installer,mrp:0
msgid ""
"Helps you manage your manufacturing processes and generate reports on those "
"processes."
msgstr ""
msgstr "يساعدك على إدارة عمليات التصنيع وإصدار تقارير عن هذه العمليات."
#. module: base_setup
#: help:base.setup.installer,marketing:0
msgid "Helps you manage your marketing campaigns step by step."
msgstr ""
msgstr "يساعدك على إدارة حملاتك التسويقية خطوة بخطوة."
#. module: base_setup
#: view:base.setup.config:0
@@ -81,7 +81,7 @@ msgstr "نقاط البيع"
#. module: base_setup
#: field:base.setup.installer,association:0
msgid "Associations"
msgstr ""
msgstr "الجمعيات"
#. module: base_setup
#: help:base.setup.installer,account_accountant:0
@@ -89,6 +89,8 @@ msgid ""
"Helps you handle your accounting needs, if you are not an accountant, we "
"suggest you to install only the Invoicing "
msgstr ""
"يساعدك على التعامل مع احتياجاتك المحاسبية. إذا لم تكن محاسباً، نقترح تثبيت "
"\"الفواتير\" فقط "
#. module: base_setup
#: code:addons/base_setup/__init__.py:50
@@ -100,33 +102,33 @@ msgstr "تمت إضافة المستخدمين التالين: \n"
#: field:base.setup.company,progress:0
#: field:base.setup.installer,progress:0
msgid "Configuration Progress"
msgstr ""
msgstr "تقدم الإعدادات"
#. module: base_setup
#: field:base.setup.company,rml_footer2:0
msgid "Report Footer 2"
msgstr ""
msgstr "تذييل تقرير 2"
#. module: base_setup
#: field:base.setup.company,currency:0
#: model:ir.model,name:base_setup.model_res_currency
msgid "Currency"
msgstr ""
msgstr "العملة"
#. module: base_setup
#: field:base.setup.company,state_id:0
msgid "Fed. State"
msgstr ""
msgstr "المحافظة / الولاية"
#. module: base_setup
#: field:base.setup.installer,marketing:0
msgid "Marketing"
msgstr ""
msgstr "التسويق"
#. module: base_setup
#: field:base.setup.company,company_id:0
msgid "Company"
msgstr ""
msgstr "الشركة"
#. module: base_setup
#: field:base.setup.installer,sale:0
@@ -139,13 +141,15 @@ msgid ""
"Lets you install various interesting but non-essential tools like Survey, "
"Lunch and Ideas box."
msgstr ""
"يمكّنك من تثبيت عدد من الأدوات المفيدة ولكن غير الضرورية، مثل الاستبانات "
"والوجبات وصندوق الأفكار."
#. module: base_setup
#: view:base.setup.config:0
msgid ""
"You can start configuring the system or connect directly to the database as "
"an administrator."
msgstr ""
msgstr "يمكنك الشروع في إعداد النظام أو استخدام النظام مباشرة كمدير."
#. module: base_setup
#: field:base.setup.installer,report_designer:0
@@ -155,12 +159,12 @@ msgstr "تقارير متقدمة"
#. module: base_setup
#: field:base.setup.company,phone:0
msgid "Phone"
msgstr ""
msgstr "هاتف"
#. module: base_setup
#: view:base.setup.company:0
msgid "res_config_contents"
msgstr ""
msgstr "res_config_contents"
#. module: base_setup
#: view:base.setup.company:0
@@ -168,16 +172,18 @@ msgid ""
"Your company information will be used to personalize documents issued with "
"OpenERP such as invoices, sales orders and much more."
msgstr ""
"سيتمّ استخدام معلومات شركتك لتخصيص المستندات التي يصدرها OpenERP مثل "
"الفواتير وأوامر المبيعات وغير ذلك الكثير."
#. module: base_setup
#: view:base.setup.installer:0
msgid "title"
msgstr ""
msgstr "العنوان"
#. module: base_setup
#: field:base.setup.installer,knowledge:0
msgid "Knowledge Management"
msgstr ""
msgstr "إدارة المعارف"
#. module: base_setup
#: model:ir.module.module,description:base_setup.module_meta_information
@@ -197,6 +203,19 @@ msgid ""
" footer, the account chart to install and the language.\n"
" "
msgstr ""
"\n"
" هذا البرنامج يشمل إعدادات النظام ليساعد المستخدم\n"
" لإعداد النظام أثناء إنشاءه قاعدة بيانات جديدة.\n"
"\n"
" سيسمح لك بإختيار من قائمة معرفة لتنصيب:\n"
" * الحد الأدني للنظام\n"
" * برامج المحاسبة فقط\n"
" * برامج القطاع الخدمي\n"
" * برامج القطاع التصنيعي\n"
"\n"
" بالإضافة إلي مساعدتك علي تحديد نوع شركتك، اسمها\n"
" و تعريف نوع الدليل المحاسبي و اللغة.\n"
" "
#. module: base_setup
#: help:base.setup.installer,product_expiry:0
@@ -204,6 +223,8 @@ msgid ""
"Installs a preselected set of OpenERP applications which will help you "
"manage your industry."
msgstr ""
"يقوم بتثبيت مجموعة من تطبيقات OpenERP مختارة مسبقاً تساعدك على إدارة نوع "
"الصناعة الخاص بك."
#. module: base_setup
#: help:base.setup.installer,project:0
@@ -211,16 +232,17 @@ msgid ""
"Helps you manage your projects and tasks by tracking them, generating "
"plannings, etc..."
msgstr ""
"يساعدك على إدارة مشاريعك ومهامك عن طريق متابعتهم وإصدار الخطط وما إلى ذلك."
#. module: base_setup
#: field:base.setup.company,name:0
msgid "Company Name"
msgstr ""
msgstr "اسم الشركة"
#. module: base_setup
#: view:base.setup.config:0
msgid "Skip Configuration Wizards"
msgstr ""
msgstr "تجاوز معالجات الإعدادات"
#. module: base_setup
#: help:base.setup.installer,hr:0
@@ -228,6 +250,8 @@ msgid ""
"Helps you manage your human resources by encoding your employees structure, "
"generating work sheets, tracking attendance and more."
msgstr ""
"يساعدك على إدارة مواردك البشرية عن طريق فهم هيكل الموظفين وإصدار أوراق العمل "
"ومتابعة الحضور وغير ذلك."
#. module: base_setup
#: help:base.setup.installer,account_voucher:0
@@ -235,11 +259,13 @@ msgid ""
"Allows you to create your invoices and track the payments. It is an easier "
"version of the accounting module for managers who are not accountants."
msgstr ""
"يمكّنك من إصدار الفواتير و متابعة المدفوعات. يعدّ نسخة مبسطة من "
"\"المحاسبة\" مناسبة للمدراء غير المحاسِبين."
#. module: base_setup
#: model:ir.model,name:base_setup.model_base_setup_company
msgid "base.setup.company"
msgstr ""
msgstr "base.setup.company"
#. module: base_setup
#: help:base.setup.installer,purchase:0
@@ -247,6 +273,8 @@ msgid ""
"Helps you manage your purchase-related processes such as requests for "
"quotations, supplier invoices, etc..."
msgstr ""
"يساعدك على إدارة العمليات المتعلقة بالشراء مثل طلبات التسعير وفواتير "
"الموردين وما إلى ذلك."
#. module: base_setup
#: help:base.setup.company,rml_footer2:0
@@ -255,26 +283,29 @@ msgid ""
"We suggest you to put bank information here:\n"
"IBAN: BE74 1262 0121 6907 - SWIFT: CPDF BE71 - VAT: BE0477.472.701"
msgstr ""
"ستظهر هذه الجملة أسفل التقارير.\n"
"نقترح عليك وضع معلوماتك المصرفية هنا:\n"
"IBAN: BE74 1262 0121 6907 - SWIFT: CPDF BE71 - VAT: BE0477.472.701"
#. module: base_setup
#: field:base.setup.company,street2:0
msgid "Street 2"
msgstr ""
msgstr "العنوان 2"
#. module: base_setup
#: model:ir.model,name:base_setup.model_base_setup_installer
msgid "base.setup.installer"
msgstr ""
msgstr "base.setup.installer"
#. module: base_setup
#: field:base.setup.company,country_id:0
msgid "Country"
msgstr ""
msgstr "الدولة"
#. module: base_setup
#: model:ir.actions.act_window,name:base_setup.action_base_setup
msgid "Setup"
msgstr ""
msgstr "إعداد"
#. module: base_setup
#: field:base.setup.installer,account_accountant:0
@@ -289,7 +320,7 @@ msgstr ""
#. module: base_setup
#: field:base.setup.company,zip:0
msgid "Zip Code"
msgstr ""
msgstr "الرمز البريدي"
#. module: base_setup
#: view:base.setup.config:0
@@ -301,7 +332,7 @@ msgstr "بدء الإعداد"
msgid ""
"Lets you install addons geared towards sharing knowledge with and between "
"your employees."
msgstr ""
msgstr "يمكّنك من تثبيت إضافات غرضها نشر ومشاركة المعارف بين موظفيك."
#. module: base_setup
#: view:base.setup.installer:0
@@ -309,6 +340,8 @@ msgid ""
"Select the Applications you want your system to cover. If you are not sure "
"about your exact needs at this stage, you can easily install them later."
msgstr ""
"اختر التطبيقات الي تريد أن يتضمنها النظام. إذا لم تكن متأكداً من احتياجاتك "
"بدقة في هذه المرحلة، يمكنك تثبيت أي من التطبيقات بسهولة لاحقاً."
#. module: base_setup
#: view:base.setup.company:0
@@ -319,7 +352,7 @@ msgstr "إعدادات الشركة"
#. module: base_setup
#: field:base.setup.company,logo:0
msgid "Logo"
msgstr ""
msgstr "الشعار"
#. module: base_setup
#: help:base.setup.installer,point_of_sale:0
@@ -328,6 +361,8 @@ msgid ""
"simplified payment mode encoding, automatic picking lists generation and "
"more."
msgstr ""
"يساعدك على تشغيل نقاط البيع بكفاءة عن طريق تسجيل عمليات البيع بسرعة وتوفير "
"وضع مبسط للدفع وإصدار تلقائي لقوائم الالتقاط وغير ذلك."
#. module: base_setup
#: field:base.setup.installer,purchase:0
@@ -337,7 +372,7 @@ msgstr "إدارة المشتريات"
#. module: base_setup
#: help:base.setup.installer,sale:0
msgid "Helps you handle your quotations, sale orders and invoicing."
msgstr ""
msgstr "يساعدك على التعامل مع التسعيرات وأوامر المبيعات والفواتير."
#. module: base_setup
#: field:base.setup.installer,stock:0
@@ -347,7 +382,7 @@ msgstr "إدارة المخازن و المستودعات"
#. module: base_setup
#: field:base.setup.installer,project:0
msgid "Project Management"
msgstr "إدارة المشروعات"
msgstr "إدارة المشاريع"
#. module: base_setup
#: field:base.setup.config,installed_users:0
@@ -357,7 +392,7 @@ msgstr "المستخدمون المعرّفون"
#. module: base_setup
#: view:base.setup.config:0
msgid "New Database"
msgstr ""
msgstr "قاعدة بيانات جديدة"
#. module: base_setup
#: field:base.setup.installer,crm:0
@@ -370,6 +405,8 @@ msgid ""
"Installs a preselected set of OpenERP applications selected to help you "
"manage your auctions as well as the business processes around them."
msgstr ""
"يقوم بتثبيت مجموعة من تطبيقات OpenERP مختارة مسبقاً لتساعدك على إدارة "
"المزادات والعمليات المتعلقة بها."
#. module: base_setup
#: help:base.setup.company,rml_header1:0
@@ -378,18 +415,21 @@ msgid ""
"We suggest you to put a slogan here:\n"
"\"Open Source Business Solutions\"."
msgstr ""
"ستظهر هذه الجملة في الزاوية اليمنى في رأس كل تقرير.\n"
"نقترح عليك وضع شعار هنا:\n"
"\"حلول أعمال مفتوحة المصدر\"."
#. module: base_setup
#: help:base.setup.installer,report_designer:0
msgid ""
"Lets you install various tools to simplify and enhance OpenERP's report "
"creation."
msgstr ""
msgstr "يمكّنك من تثبيت عدة أدوات تسهل وتحسن إنشاء التقارير."
#. module: base_setup
#: field:base.setup.company,rml_header1:0
msgid "Report Header"
msgstr ""
msgstr "رأس التقرير"
#. module: base_setup
#: view:base.setup.config:0
@@ -401,7 +441,7 @@ msgstr "معلومات عن قاعدة البيانات الجديدة"
#: field:base.setup.config,config_logo:0
#: field:base.setup.installer,config_logo:0
msgid "Image"
msgstr ""
msgstr "صورة"
#. module: base_setup
#: field:base.setup.installer,product_expiry:0
@@ -411,7 +451,7 @@ msgstr "الصناعات الغذائية"
#. module: base_setup
#: field:base.setup.installer,mrp:0
msgid "Manufacturing"
msgstr ""
msgstr "التصنيع"
#. module: base_setup
#: view:base.setup.company:0
@@ -425,6 +465,9 @@ msgid ""
"We suggest you to write legal sentences here:\n"
"Web: http://openerp.com - Fax: +32.81.73.35.01 - Fortis Bank: 126-2013269-07"
msgstr ""
"ستظهر هذه الجملة في أسفل التقارير.\n"
"نقترح عليك كتابة معلومات قانونية هنا:\n"
"موقع: http://openerp.com - فاكس: +32.81.73.35.01 - بنك فوتيس: 126-2013269-07"
#. module: base_setup
#: field:base.setup.company,website:0
@@ -434,12 +477,12 @@ msgstr "الموقع الإلكتروني للشركة"
#. module: base_setup
#: view:base.setup.installer:0
msgid "Install Specific Industry Applications"
msgstr "نصّب تطبيقات صناعات معينة"
msgstr "ثبّت تطبيقات صناعات معينة"
#. module: base_setup
#: field:base.setup.company,street:0
msgid "Street"
msgstr ""
msgstr "العنوان"
#. module: base_setup
#: view:base.setup.company:0
@@ -455,7 +498,7 @@ msgstr "مثال: http://openerp.com"
#: view:base.setup.installer:0
#: model:ir.actions.act_window,name:base_setup.action_base_setup_installer
msgid "Install Applications"
msgstr "تنصيب التطبيقات"
msgstr "تثبيت تطبيقات"
#. module: base_setup
#: help:base.setup.installer,crm:0
@@ -464,6 +507,9 @@ msgid ""
"or issues. Can automatically send reminders, escalate requests or trigger "
"business-specific actions based on standard events."
msgstr ""
"يساعدك على متابعة وإدارة علاقات العملاء مثل الفرص والطلبات والشكاوى. يوفر "
"إمكانيات تلقائية لإرسال رسائل تذكير، وتصعيد طلبات، وابتداء إجراءات مخصصة "
"بناء على أحداث قياسية."
#. module: base_setup
#: help:base.setup.installer,stock:0
@@ -471,6 +517,8 @@ msgid ""
"Helps you manage your inventory and main stock operations: delivery orders, "
"receptions, etc."
msgstr ""
"يساعدك على إدارة العمليات المتعلقة بكميات المخزون مثل أوامر التسليم "
"والاستلام وما إلى ذلك."
#. module: base_setup
#: model:ir.module.module,shortdesc:base_setup.module_meta_information
@@ -483,11 +531,13 @@ msgid ""
"Installs a preselected set of OpenERP applications which will help you "
"manage your association more efficiently."
msgstr ""
"يقوم بتثبيت مجموعة من تطبيقات OpenERP مختارة مسبقاً لتساعدك على إدارة جمعيتك "
"بكفاءة أعلى."
#. module: base_setup
#: model:ir.model,name:base_setup.model_base_setup_config
msgid "base.setup.config"
msgstr ""
msgstr "base.setup.config"
#~ msgid "Point of Sales"
#~ msgstr "نقاط البيع"
+16 -16
View File
@@ -56,14 +56,14 @@ class board_board(osv.osv):
#End Loop
arch = """<?xml version="1.0"?>
<form string="My Board">
<hpaned>
<child1>
<board style="1-1">
<column>
%s
</child1>
<child2>
</column>
<column>
%s
</child2>
</hpaned>
</column>
</board>
</form>""" % ('\n'.join(left), '\n'.join(right))
return arch
@@ -134,10 +134,10 @@ class board_board(osv.osv):
res['arch'] = self._arch_preprocessing(cr, user, res['arch'], context=context)
res['toolbar'] = {'print': [], 'action': [], 'relate': []}
return res
def _arch_preprocessing(self, cr, user, arch, context=None):
from lxml import etree
def _arch_preprocessing(self, cr, user, arch, context=None):
from lxml import etree
def remove_unauthorized_children(node):
for child in node.iterchildren():
if child.tag=='action' and child.get('invisible'):
@@ -145,17 +145,17 @@ class board_board(osv.osv):
else:
child=remove_unauthorized_children(child)
return node
def encode(s):
if isinstance(s, unicode):
return s.encode('utf8')
return s
archnode = etree.fromstring(encode(arch))
archnode = etree.fromstring(encode(arch))
return etree.tostring(remove_unauthorized_children(archnode),pretty_print=True)
_columns = {
'name': fields.char('Dashboard', size=64, required=True),
+12 -21
View File
@@ -78,31 +78,22 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Statistics Dashboard">
<hpaned>
<child1>
<action
string="Opportunities by Categories"
name="%(act_oppor_categ)d"
colspan="4"
height="200"
width="250"/>
<board style="1-1">
<column>
<action string="Opportunities by Categories"
name="%(act_oppor_categ)d"/>
<action
string="Opportunities by Stage"
name="%(act_opportunity_stage)d"
height="200"
colspan="4"/>
<action string="Opportunities by Stage"
name="%(act_opportunity_stage)d"/>
</child1>
<child2>
</column>
<column>
<action
string="Planned Revenue by Stage and User"
name="%(act_oppor_stage_user)d"
colspan="4"/>
<action string="Planned Revenue by Stage and User"
name="%(act_oppor_stage_user)d"/>
</child2>
</hpaned>
</column>
</board>
</form>
</field>
</record>
+13 -26
View File
@@ -116,34 +116,21 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Sales Dashboard">
<hpaned>
<child1>
<action
string="My Open Opportunities"
name="%(act_my_oppor)d"
colspan="4"
height="150"
groups="base.group_sale_salesman,base.group_sale_manager"
width="510"/>
<board style="1-1">
<column>
<action string="My Open Opportunities"
name="%(act_my_oppor)d"/>
<action
string="My Meetings"
name="%(act_my_meetings)d"
height="150"
colspan="4"/>
</child1>
<child2>
<action
string="My Planned Revenues by Stage"
name="%(act_my_oppor_stage)d"
groups="base.group_sale_salesman,base.group_sale_manager"
colspan="4"/>
<action
string="My Win/Lost Ratio"
name="%(act_sales_pipeline)d"
groups="base.group_sale_salesman,base.group_sale_manager"
colspan="4"/>
</child2>
</hpaned>
name="%(act_my_meetings)d"/>
</column>
<column>
<action string="My Planned Revenues by Stage"
name="%(act_my_oppor_stage)d"/>
<action string="My Win/Lost Ratio"
name="%(act_sales_pipeline)d"/>
</column>
</board>
</form>
</field>
</record>
+3 -1
View File
@@ -155,7 +155,7 @@ class crm_case_categ(osv.osv):
"""Finds id for case object"""
object_id = context and context.get('object_id', False) or False
ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', object_id)])
return ids and ids[0]
return ids and ids[0] or False
_defaults = {
'object_id' : _find_object_id
@@ -241,6 +241,8 @@ class crm_base(object):
address = self.pool.get('res.partner.address').browse(cr, uid, add)
data['value'] = {'email_from': address and address.email or False ,
'phone': address and address.phone or False}
if 'phone' not in self._columns:
del data['value']['phone']
return data
def onchange_partner_id(self, cr, uid, ids, part, email=False):
+13 -14
View File
@@ -72,6 +72,17 @@
<field name="arch" type="xml">
<search string="Leads Analysis">
<group>
<filter string="Year" icon="terp-go-year" name="year"
domain="[('create_date','&lt;=', time.strftime('%%Y-%%m-%%d')),('create_date','&gt;=',time.strftime('%%Y-01-01'))]"
help="Leads/Opportunities created in current year"/>
<separator orientation="vertical" />
<filter string="Month" icon="terp-go-month" name="this_month"
domain="[('create_date','&lt;=',(datetime.date.today()+relativedelta(day=31)).strftime('%%Y-%%m-%%d')),('create_date','&gt;=',(datetime.date.today()-relativedelta(day=1)).strftime('%%Y-%%m-%%d'))]"
help="Leads/Opportunities created in current month"/>
<filter icon="terp-go-month" string="Month-1"
domain="[('create_date','&lt;=', (datetime.date.today() - relativedelta(day=31, months=1)).strftime('%%Y-%%m-%%d')),('create_date','&gt;=',(datetime.date.today() - relativedelta(day=1,months=1)).strftime('%%Y-%%m-%%d'))]"
help="Leads/Opportunities created in last month"/>
<separator orientation="vertical" />
<filter icon="terp-personal"
name="lead"
string="Lead"
@@ -83,18 +94,6 @@
domain="[('type','=','opportunity')]"
help="Show only opportunity"/>
<separator orientation="vertical" />
<filter string=" Year " icon="terp-go-year"
domain="[('create_date','&lt;=', time.strftime('%%Y-%%m-%%d')),('create_date','&gt;=',time.strftime('%%Y-01-01'))]"
help="Leads/Opportunities created in current year"/>
<separator orientation="vertical" />
<filter string="Month" icon="terp-go-month" name="this_month"
domain="[('create_date','&lt;=',(datetime.date.today()+relativedelta(day=31)).strftime('%%Y-%%m-%%d')),('create_date','&gt;=',(datetime.date.today()-relativedelta(day=1)).strftime('%%Y-%%m-%%d'))]"
help="Leads/Opportunities created in current month"/>
<filter icon="terp-go-month" string=" Month-1 "
domain="[('create_date','&lt;=', (datetime.date.today() - relativedelta(day=31, months=1)).strftime('%%Y-%%m-%%d')),('create_date','&gt;=',(datetime.date.today() - relativedelta(day=1,months=1)).strftime('%%Y-%%m-%%d'))]"
help="Leads/Opportunities created in last month"/>
<separator orientation="vertical" />
<filter icon="terp-check"
string="New"
domain="[('state','=','draft')]"
@@ -221,7 +220,7 @@
<field name="name">Leads Analysis</field>
<field name="res_model">crm.lead.report</field>
<field name="view_type">form</field>
<field name="context">{'search_default_lead': 1, "search_default_user":1, "search_default_this_month":1, 'group_by_no_leaf':1, 'group_by':[]}</field>
<field name="context">{'search_default_year': 1,'search_default_lead': 1, "search_default_user":1, "search_default_this_month":1, 'group_by_no_leaf':1, 'group_by':[]}</field>
<field name="view_mode">tree,graph</field>
<field name="domain">[]</field>
<field name="help">Leads Analysis allows you to check different CRM related information. Check for treatment delays, number of responses given and emails sent. You can sort out your leads analysis by different groups to get accurate grained analysis.</field>
@@ -243,7 +242,7 @@
<field name="name">Opportunities Analysis</field>
<field name="res_model">crm.lead.report</field>
<field name="view_type">form</field>
<field name="context">{"search_default_opportunity":1,"search_default_opportunity": 1, "search_default_user":1,"search_default_this_month":1,'group_by_no_leaf':1,'group_by':[]}</field>
<field name="context">{"search_default_year":1,"search_default_opportunity":1, "search_default_user":1,"search_default_this_month":1,'group_by_no_leaf':1,'group_by':[]}</field>
<field name="view_mode">tree,graph</field>
<field name="help">Opportunities Analysis gives you an instant access to your opportunities with information such as the expected revenue, planned cost, missed deadlines or the number of interactions per opportunity. This report is mainly used by the sales manager in order to do the periodic review with the teams of the sales pipeline.</field>
</record>
@@ -52,15 +52,14 @@
<field name="arch" type="xml">
<search string="Search">
<group>
<filter string=" Year " icon="terp-go-year"
<filter string="Year" icon="terp-go-year" name="year"
domain="[('create_date','&lt;=', time.strftime('%%Y-%%m-%%d')),('create_date','&gt;=',time.strftime('%%Y-01-01'))]"
help="Phone calls made in current year"/>
<filter string=" Month " icon="terp-go-month" name="This Month"
<separator orientation="vertical" />
<filter string="Month" icon="terp-go-month" name="This Month"
domain="[('create_date','&lt;=',(datetime.date.today()+relativedelta(day=31)).strftime('%%Y-%%m-%%d')),('create_date','&gt;=',(datetime.date.today()-relativedelta(day=1)).strftime('%%Y-%%m-%%d'))]"
help="Phone calls made in current month"/>
<filter icon="terp-go-month" string=" Month-1 "
<filter icon="terp-go-month" string="Month-1"
domain="[('create_date','&lt;=', (datetime.date.today() - relativedelta(day=31, months=1)).strftime('%%Y-%%m-%%d')),('create_date','&gt;=',(datetime.date.today() - relativedelta(day=1,months=1)).strftime('%%Y-%%m-%%d'))]"
help="Phone calls made in last month"/>
<separator orientation="vertical" />
@@ -135,7 +134,7 @@
<field name="res_model">crm.phonecall.report</field>
<field name="view_type">form</field>
<field name="view_mode">tree,graph</field>
<field name="context">{"search_default_User":1,"search_default_This Month":1,'group_by_no_leaf':1,'group_by':[]}</field>
<field name="context">{"search_default_year":1,"search_default_User":1,"search_default_This Month":1,'group_by_no_leaf':1,'group_by':[]}</field>
<field name="view_id" ref="view_report_crm_phonecall_tree"/>
<field name="search_view_id" ref="view_report_crm_phonecall_filter"/>
<field name="help">From this report, you can analyse the performance of your sales team, based on their phone calls. You can group or filter the information according to several criteria and drill down the information, by adding more groups in the report.</field>
+2 -3
View File
@@ -105,8 +105,7 @@ class crm_claim(crm.crm_case, osv.osv):
if not part:
return {'value': {'partner_address_id': False,
'email_from': False,
'partner_phone': False,
'partner_mobile': False
'partner_phone': False
}}
addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact'])
data = {'partner_address_id': addr['contact']}
@@ -121,7 +120,7 @@ class crm_claim(crm.crm_case, osv.osv):
if not add:
return {'value': {'email_from': False}}
address = self.pool.get('res.partner.address').browse(cr, uid, add)
return {'value': {'email_from': address.email, 'partner_phone': address.phone, 'partner_mobile': address.mobile}}
return {'value': {'email_from': address.email, 'partner_phone': address.phone}}
def case_open(self, cr, uid, ids, *args):
"""Opens Claim"""
+5 -2
View File
@@ -21,9 +21,10 @@
{
'name': 'To Do List for CRM',
'name': 'To Do List',
'version': '1.0',
'category': 'Sales',
'category': 'Customer Relationship Management',
'complexity': "easy",
'description': """
Todo list for CRM leads and opportunities.
""",
@@ -35,4 +36,6 @@ Todo list for CRM leads and opportunities.
'demo': [
'crm_todo_demo.xml',
],
'installable': True,
'active': False,
}
+5 -3
View File
@@ -46,13 +46,15 @@
<record model="ir.actions.act_window" id="crm_todo_action">
<field name="name">Todo List</field>
<field name="name">My Tasks</field>
<field name="res_model">project.task</field>
<field name="context">{'set_editable':True,'set_visible':True,'gtd':True,'user_invisible':True, 'search_default_user_id':uid, "search_default_current": 1, "search_default_open": 1, "lead_id": 1}</field>
<field name="context">{'set_editable':True,'set_visible':True,'gtd':True,'user_invisible':True, "search_default_open": 1, "lead_id": 1}</field>
<field name="search_view_id" ref="project_gtd.view_task_gtd_search"/>
<field name="domain">[('user_id','=',uid)]</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,calendar,gantt,graph,kanban</field>
</record>
<menuitem name="Tasks"
<menuitem
id="menu_crm_todo"
parent="base.menu_sales"
action="crm_todo_action"/>
+20 -28
View File
@@ -7,30 +7,25 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Document board">
<hpaned position="100">
<child1>
<action
string="New Files"
<board style="1-1" >
<column>
<action string="New Files"
name="%(document.action_view_all_document_tree1)d"
view_mode="tree,form"/>
<action
string="File Size by Month"
<action string="File Size by Month"
name="%(document.action_view_size_month)d"
view_mode="graph,tree"/>
</child1>
<child2>
<action
string="Files by Resource Type"
</column>
<column>
<action string="Files by Resource Type"
name="%(document.action_view_document_by_resourcetype_graph)d"
view_mode="graph,tree"
/>
view_mode="graph,tree"/>
<action
string="Files by Partner"
<action string="Files by Partner"
name="%(document.action_view_files_by_partner)d"
view_mode="graph,tree"/>
</child2>
</hpaned>
</column>
</board>
</form>
</field>
</record>
@@ -70,26 +65,23 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Document board">
<hpaned position="100">
<child1>
<action
string="Wall of Shame"
<board style="1-1">
<column>
<action string="Wall of Shame"
name="%(document.action_view_wall)d"
view_mode="tree"/>
</child1>
<child2>
<action
string="Files by Users"
</column>
<column>
<action string="Files by Users"
name="%(document.action_view_user_graph)d"
view_mode="graph,tree"/>
<action
string="Files by Month"
<action string="Files by Month"
name="%(document.action_view_files_by_month_graph)d"
view_mode="graph,tree"/>
</child2>
</hpaned>
</column>
</board>
</form>
</field>
</record>
-1
View File
@@ -372,7 +372,6 @@
<field name="model" eval="'document.directory'"/>
<field name="name">Browse Files</field>
<field name="value" eval="'ir.actions.act_window,%d'%action_document_file_directory_form"/>
<field name="object" eval="True"/>
</record>
<record model="ir.ui.view" id="view_attachment_form_inherit">
+27
View File
@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2011 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 models
import edi_service
from models.edi import EDIMixin, edi_document
# web
import controllers
+57
View File
@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2011 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/>.
#
##############################################################################
{
'name': 'Electronic Data Interchange (EDI)',
'version': '1.0',
'category': 'Tools',
'complexity': "easy",
'description': """
Provides a common EDI platform that other Applications can use
==============================================================
OpenERP specifies a generic EDI format for exchanging business
documents between different systems, and provides generic
mechanisms to import and export them.
More details about OpenERP's EDI format may be found in the
technical OpenERP documentation at http://doc.openerp.com
""",
'author': 'OpenERP SA',
'website': 'http://www.openerp.com',
'depends': ['base', 'email_template'],
'data': [
'security/ir.model.access.csv',
],
'test': [
'test/edi_partner_test.yml',
],
'js': [
'static/src/js/edi.js',
],
"css": [
"static/src/css/edi.css"
],
'installable': True,
'active': False,
'certificate': '002046536359186',
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+1
View File
@@ -0,0 +1 @@
import main
+84
View File
@@ -0,0 +1,84 @@
import json
import textwrap
import simplejson
import werkzeug.wrappers
import web.common.http as openerpweb
import web.controllers.main
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'):
self.wc = openerpweb.controllers_path['/web/webclient']
d = {}
d["js"] = "\n".join('<script type="text/javascript" src="%s"></script>'%i for i in self.wc.manifest_list(req, mods, 'js'))
d["css"] = "\n".join('<link rel="stylesheet" href="%s">'%i for i in self.wc.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"] = 'new s.edi.EdiView(null,"%s","%s").appendTo($("body"));'%(db,token)
r = web.controllers.main.html_template % d
return r
@openerpweb.httprequest
def import_url(self, req, url):
d = self.template(req)
d["init"] = 'new s.edi.EdiImport(null,"%s").appendTo($("body"));'%(url)
r = web.controllers.main.html_template % d
return r
@openerpweb.httprequest
def download(self, req, db, token):
result = req.session.proxy('edi').get_edi_document(db, token)
response = werkzeug.wrappers.Response( result, headers=[('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', len(result))])
return response
@openerpweb.httprequest
def download_attachment(self, req, db, token):
result = req.session.proxy('edi').get_edi_document(db, token)
doc = json.loads(result)[0]
attachment = doc['__attachments'] and doc['__attachments'][0]
if attachment:
result = attachment["content"].decode('base64')
import email.Utils as utils
# Encode as per RFC 2231
filename_utf8 = attachment['file_name']
filename_encoded = "%s=%s" % ('filename*',
utils.encode_rfc2231(filename_utf8, 'utf-8'))
response = werkzeug.wrappers.Response(result, headers=[('Content-Type', 'application/pdf'),
('Content-Disposition', 'inline; ' + filename_encoded),
('Content-Length', len(result))])
return response
@openerpweb.httprequest
def binary(self, req, db, token, field_path="company_address.logo", content_type='image/png'):
result = req.session.proxy('edi').get_edi_document(db, token)
doc = json.loads(result)[0]
for name in field_path.split("."):
doc = doc[name]
result = doc.decode('base64')
response = werkzeug.wrappers.Response(result, headers=[('Content-Type', content_type),
('Content-Length', len(result))])
return response
@openerpweb.jsonrequest
def get_edi_document(self, req, db, token):
result = req.session.proxy('edi').get_edi_document(db, token)
return json.loads(result)
@openerpweb.jsonrequest
def import_edi_url(self, req, url):
result = req.session.proxy('edi').import_edi_url(req.session._db, req.session._uid, req.session._password, url)
if len(result) == 1:
return {"action": web.controllers.main.clean_action(req, result[0][2])}
return True
#
+70
View File
@@ -0,0 +1,70 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2011 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
import netsvc
import openerp
_logger = logging.getLogger('edi.service')
class edi(netsvc.ExportService):
def __init__(self, name="edi"):
netsvc.ExportService.__init__(self, name)
def _edi_dispatch(self, db_name, method_name, *method_args):
try:
registry = openerp.modules.registry.RegistryManager.get(db_name)
assert registry, 'Unknown database %s' % db_name
edi_document = registry['edi.document']
cr = registry.db.cursor()
res = None
res = getattr(edi_document, method_name)(cr, *method_args)
cr.commit()
except Exception:
_logger.exception('Failed to execute EDI method %s with args %r', method_name, method_args)
raise
finally:
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)
def exp_import_edi_url(self, db_name, uid, passwd, edi_url, context=None):
return self._edi_dispatch(db_name, 'import_edi', uid, None, edi_url)
def dispatch(self, method, params):
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)
return fn(*params)
edi()
+25
View File
@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2011 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 edi
import res_partner
import res_company
import res_currency
+684
View File
@@ -0,0 +1,684 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2011 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 base64
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
from tools.translate import _
from tools.safe_eval import safe_eval as eval
EXTERNAL_ID_PATTERN = re.compile(r'^([^.:]+)(?::([^.]+))?\.(\S+)$')
EDI_VIEW_WEB_URL = '%s/edi/view?debug=1&db=%s&token=%s'
EDI_PROTOCOL_VERSION = 1 # arbitrary ever-increasing version number
EDI_GENERATOR = 'OpenERP ' + release.major_version
EDI_GENERATOR_VERSION = release.version_info
def split_external_id(ext_id):
match = EXTERNAL_ID_PATTERN.match(ext_id)
assert match, \
_("'%s' is an invalid external ID") % (ext_id)
return {'module': match.group(1),
'db_uuid': match.group(2),
'id': match.group(3),
'full': match.group(0)}
def safe_unique_id(database_id, model, record_id):
"""Generate a unique string to represent a (database_uuid,model,record_id) pair
without being too long, and with a very low probability of collisions.
"""
msg = "%s-%s-%s-%s" % (time.time(), database_id, model, record_id)
digest = hashlib.sha1(msg).digest()
# fold the sha1 20 bytes digest to 9 bytes
digest = ''.join(chr(ord(x) ^ ord(y)) for (x,y) in zip(digest[:9], digest[9:-2]))
# b64-encode the 9-bytes folded digest to a reasonable 12 chars ASCII ID
digest = base64.urlsafe_b64encode(digest)
return '%s-%s' % (model.replace('.','_'), digest)
def last_update_for(record):
"""Returns the last update timestamp for the given record,
if available, otherwise False
"""
if record._model._log_access:
record_log = record.perm_read()[0]
return record_log.get('write_date') or record_log.get('create_date') or False
return False
_logger = logging.getLogger('edi')
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!')
]
def new_edi_token(self, cr, uid, record):
"""Return a new, random unique token to identify this model record,
and to be used as token when exporting it as an EDI document.
:param browse_record record: model record for which a token is needed
"""
db_uuid = self.pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid')
edi_token = hashlib.sha256('%s-%s-%s-%s' % (time.time(), db_uuid, record._name, record.id)).hexdigest()
return edi_token
def serialize(self, edi_documents):
"""Serialize the given EDI document structures (Python dicts holding EDI data),
using JSON serialization.
:param [dict] edi_documents: list of EDI document structures to serialize
:return: UTF-8 encoded string containing the serialized document
"""
serialized_list = json.dumps(edi_documents)
return serialized_list
def generate_edi(self, cr, uid, records, context=None):
"""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`.
:param list(browse_record) records: records to export as EDI
:return: UTF-8 encoded string containing the serialized records
"""
edi_list = []
for record in records:
record_model_obj = self.pool.get(record._name)
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`.
:param edi_documents: list of Python dicts containing the deserialized
version of EDI documents
:return: list of (model, id, action) tuple containing the model and database ID
of all records that were imported in the system, plus a suggested
action definition dict for displaying each document.
"""
ir_module = self.pool.get('ir.module.module')
res = []
for edi_document in edi_documents:
module = edi_document.get('__import_module') or edi_document.get('__module')
assert module, 'a `__module` or `__import_module` attribute is required in each EDI document'
if module != 'base' and not ir_module.search(cr, uid, [('name','=',module),('state','=','installed')]):
raise osv.except_osv(_('Missing Application'),
_("The document you are trying to import requires the OpenERP `%s` application. "
"You can install it by connecting as the administrator and opening the configuration assistant.")%(module,))
model = edi_document.get('__import_model') or edi_document.get('__model')
assert model, 'a `__model` or `__import_model` attribute is required in each EDI document'
model_obj = self.pool.get(model)
assert model_obj, 'model `%s` cannot be found, despite module `%s` being available - '\
'this EDI document seems invalid or unsupported' % (model,module)
record_id = model_obj.edi_import(cr, uid, edi_document, context=context)
record_action = model_obj._edi_record_display_action(cr, uid, record_id, context=context)
res.append((model, record_id, record_action))
return res
def deserialize(self, edi_documents_string):
"""Return deserialized version of the given EDI Document string.
:param str|unicode edi_documents_string: UTF-8 string (or unicode) containing
JSON-serialized EDI document(s)
:return: Python object representing the EDI document(s) (usually a list of dicts)
"""
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
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``)
may be retrieved, without authentication.
"""
if edi_url:
assert not edi_document, 'edi_document 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)
return self.load_edi(cr, uid, edi_documents, context=context)
class EDIMixin(object):
"""Mixin class for Model objects that want be exposed as EDI documents.
Classes that inherit from this mixin class should override the
``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
for attribute in attributes:
assert edi_document.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
# SUPERUSER based on its parameters
def _edi_external_id(self, cr, uid, record, existing_id=None, existing_module=None,
context=None):
"""Generate/Retrieve unique external ID for ``record``.
Each EDI record and each relationship attribute in it is identified by a
unique external ID, which includes the database's UUID, as a way to
refer to any record within any OpenERP instance, without conflict.
For OpenERP records that have an existing "External ID" (i.e. an entry in
ir.model.data), the EDI unique identifier for this record will be made of
"%s:%s:%s" % (module, database UUID, ir.model.data ID). The database's
UUID MUST NOT contain a colon characters (this is guaranteed by the
UUID algorithm).
For records that have no existing ir.model.data entry, a new one will be
created during the EDI export. It is recommended that the generated external ID
contains a readable reference to the record model, plus a unique value that
hides the database ID. If ``existing_id`` is provided (because it came from
an import), it will be used instead of generating a new one.
If ``existing_module`` is provided (because it came from
an import), it will be used instead of using local values.
:param browse_record record: any browse_record needing an EDI external ID
:param string existing_id: optional existing external ID value, usually coming
from a just-imported EDI record, to be used instead
of generating a new one
:param string existing_module: optional existing module name, usually in the
format ``module:db_uuid`` and coming from a
just-imported EDI record, to be used instead
of local values
:return: the full unique External ID to use for record
"""
ir_model_data = self.pool.get('ir.model.data')
db_uuid = self.pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid')
ext_id = record.get_external_id()[record.id]
if not ext_id:
ext_id = existing_id or safe_unique_id(db_uuid, record._name, record.id)
# ID is unique cross-db thanks to db_uuid (already included in existing_module)
module = existing_module or "%s:%s" % (record._original_module, db_uuid)
_logger.debug("%s: Generating new external ID `%s.%s` for %r", self._name,
module, ext_id, record)
ir_model_data.create(cr, openerp.SUPERUSER_ID,
{'name': ext_id,
'model': record._name,
'module': module,
'res_id': record.id})
else:
module, ext_id = ext_id.split('.')
if not ':' in module:
# this record was not previously EDI-imported
if not module == record._original_module:
# this could happen for data records defined in a module that depends
# on the module that owns the model, e.g. purchase defines
# product.pricelist records.
_logger.debug('Mismatching module: expected %s, got %s, for %s',
module, record._original_module, record)
# ID is unique cross-db thanks to db_uuid
module = "%s:%s" % (module, db_uuid)
return '%s.%s' % (module, ext_id)
def _edi_record_display_action(self, cr, uid, id, context=None):
"""Returns an appropriate action definition dict for displaying
the record with ID ``rec_id``.
:param int id: database ID of record to display
:return: action definition dict
"""
return {'type': 'ir.actions.act_window',
'view_mode': 'form,tree',
'view_type': 'form',
'res_model': self._name,
'res_id': id}
def edi_metadata(self, cr, uid, records, context=None):
"""Return a list containing the boilerplate EDI structures for
exporting ``records`` as EDI, including
the metadata fields
The metadata fields always include::
{
'__model': 'some.model', # record model
'__module': 'module', # require module
'__id': 'module:db-uuid:model.id', # unique global external ID for the record
'__last_update': '2011-01-01 10:00:00', # last update date in UTC!
'__version': 1, # EDI spec version
'__generator' : 'OpenERP', # EDI generator
'__generator_version' : [6,1,0], # server version, to check compatibility.
'__attachments_':
}
:param list(browse_record) records: records to export
: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:
ext_id = self._edi_external_id(cr, uid, record, context=context)
edi_dict = {
'__id': ext_id,
'__last_update': last_update_for(record),
'__model' : record._name,
'__module' : record._original_module,
'__version': EDI_PROTOCOL_VERSION,
'__generator': EDI_GENERATOR,
'__generator_version': EDI_GENERATOR_VERSION,
}
attachment_ids = ir_attachment.search(cr, uid, [('res_model','=', record._name), ('res_id', '=', record.id)])
if attachment_ids:
attachments = []
for attachment in ir_attachment.browse(cr, uid, attachment_ids, context=context):
attachments.append({
'name' : attachment.name,
'content': attachment.datas, # already base64 encoded!
'file_name': attachment.datas_fname,
})
edi_dict.update(__attachments=attachments)
results.append(edi_dict)
return results
def edi_m2o(self, cr, uid, record, context=None):
"""Return a m2o EDI representation for the given record.
The EDI format for a many2one is::
['unique_external_id', 'Document Name']
"""
edi_ext_id = self._edi_external_id(cr, uid, record, context=context)
relation_model = record._model
name = relation_model.name_get(cr, uid, [record.id], context=context)
name = name and name[0][1] or False
return [edi_ext_id, name]
def edi_o2m(self, cr, uid, records, edi_struct=None, context=None):
"""Return a list representing a O2M EDI relationship containing
all the given records, according to the given ``edi_struct``.
This is basically the same as exporting all the record using
:meth:`~.edi_export` with the given ``edi_struct``, and wrapping
the results in a list.
Example::
[ # O2M fields would be a list of dicts, with their
{ '__id': 'module:db-uuid.id', # own __id.
'__last_update': 'iso date', # update date
'name': 'some name',
#...
},
# ...
],
"""
result = []
for record in records:
result += record._model.edi_export(cr, uid, [record], edi_struct=edi_struct, context=context)
return result
def edi_m2m(self, cr, uid, records, context=None):
"""Return a list representing a M2M EDI relationship directed towards
all the given records.
This is basically the same as exporting all the record using
:meth:`~.edi_m2o` and wrapping the results in a list.
Example::
# M2M fields are exported as a list of pairs, like a list of M2O values
[
['module:db-uuid.id1', 'Task 01: bla bla'],
['module:db-uuid.id2', 'Task 02: bla bla']
]
"""
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
records, and matching the given ``edi_struct``, if provided.
:param edi_struct: if provided, edi_struct should be a dictionary
with a skeleton of the fields to export.
Basic fields can have any key as value, but o2m
values should have a sample skeleton dict as value,
to act like a recursive export.
For example, for a res.partner record::
edi_struct: {
'name': True,
'company_id': True,
'address': {
'name': True,
'street': True,
}
}
Any field not specified in the edi_struct will not
be included in the exported data. Fields with no
value (False) will be omitted in the EDI struct.
If edi_struct is omitted, no fields will be exported
"""
if edi_struct is None:
edi_struct = {}
fields_to_export = edi_struct.keys()
results = []
for record in records:
edi_dict = self.edi_metadata(cr, uid, [record], context=context)[0]
for field in fields_to_export:
column = self._all_columns[field].column
value = getattr(record, field)
if not value and value not in ('', 0):
continue
elif column._type == 'many2one':
value = self.edi_m2o(cr, uid, value, context=context)
elif column._type == 'many2many':
value = self.edi_m2m(cr, uid, value, context=context)
elif column._type == 'one2many':
value = self.edi_o2m(cr, uid, value, edi_struct=edi_struct.get(field, {}), context=context)
edi_dict[field] = value
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 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)
if len(search_results) == 1:
return model.browse(cr, uid, search_results[0][0], context=context)
return False
def _edi_generate_report_attachment(self, cr, uid, record, context=None):
"""Utility method to generate the first PDF-type report declared for the
current model with ``usage`` attribute set to ``default``.
This must be called explicitly by models that need it, usually
at the beginning of ``edi_export``, before the call to ``super()``."""
ir_actions_report = self.pool.get('ir.actions.report.xml')
matching_reports = ir_actions_report.search(cr, uid, [('model','=',self._name),
('report_type','=','pdf'),
('usage','=','default')])
if matching_reports:
report = ir_actions_report.browse(cr, uid, matching_reports[0])
report_service = 'report.' + report.report_name
service = netsvc.LocalService(report_service)
(result, format) = service.create(cr, uid, [record.id], {'model': self._name}, context=context)
eval_context = {'time': time, 'object': record}
if not report.attachment or not eval(report.attachment, eval_context):
# no auto-saving of report as attachment, need to do it manually
result = base64.b64encode(result)
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},
context=context)
def _edi_import_attachments(self, cr, uid, record_id, edi_document, context=None):
ir_attachment = self.pool.get('ir.attachment')
for attachment in edi_document.get('__attachments', []):
# check attachment data is non-empty and valid
file_data = None
try:
file_data = base64.b64decode(attachment.get('content'))
except TypeError:
pass
assert file_data, 'Incorrect/Missing attachment file content'
assert attachment.get('name'), 'Incorrect/Missing attachment name'
assert attachment.get('file_name'), 'Incorrect/Missing attachment file name'
assert attachment.get('file_name'), 'Incorrect/Missing attachment file name'
ir_attachment.create(cr, uid, {'name': attachment['name'],
'datas_fname': attachment['file_name'],
'res_model': self._name,
'res_id': record_id,
# should be pure 7bit ASCII
'datas': str(attachment['content']),
}, context=context)
def _edi_get_object_by_external_id(self, cr, uid, external_id, model, context=None):
"""Returns browse_record representing object identified by the model and external_id,
or None if no record was found with this external id.
:param external_id: fully qualified external id, in the EDI form
``module:db_uuid:identifier``.
:param model: model name the record belongs to.
"""
ir_model_data = self.pool.get('ir.model.data')
# external_id is expected to have the form: ``module:db_uuid:model.random_name``
ext_id_members = split_external_id(external_id)
db_uuid = self.pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid')
module = ext_id_members['module']
ext_id = ext_id_members['id']
modules = []
ext_db_uuid = ext_id_members['db_uuid']
if ext_db_uuid:
modules.append('%s:%s' % (module, ext_id_members['db_uuid']))
if ext_db_uuid is None or ext_db_uuid == db_uuid:
# local records may also be registered without the db_uuid
modules.append(module)
data_ids = ir_model_data.search(cr, uid, [('model','=',model),
('name','=',ext_id),
('module','in',modules)])
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
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
given model, returning the corresponding database ID:
* First, checks if the ``external_id`` is already known, in which case the corresponding
database ID is directly returned, without doing anything else;
* If the ``external_id`` is unknown, attempts to locate an existing record
with the same ``value`` via name_search(). If found, the given external_id will
be assigned to this local record (in addition to any existing one)
* 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
"""
_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)
need_new_ext_id = False
if not target:
_logger.debug("%s: Importing EDI relationship [%r,%r] - ID not found, trying name_get",
self._name, external_id, value)
target = self._edi_get_object_by_name(cr, uid, value, model, context=context)
need_new_ext_id = True
if not target:
_logger.debug("%s: Importing EDI relationship [%r,%r] - name not found, creating it!",
self._name, external_id, value)
# also need_new_ext_id here, but already been set above
model = self.pool.get(model)
res_id, name = model.name_create(cr, uid, value, context=context)
target = model.browse(cr, uid, res_id, context=context)
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
# to avoid being taken as part of the module's data, and cleanup up at next update
module = "%s:%s" % (ext_id_members['module'], ext_id_members['db_uuid'])
# create a new ir.model.data entry for this value
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.
:param dict edi_document: 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')), \
"EDI Document Model and current model do not match: '%s' (EDI) vs '%s' (current)" % \
(edi_document['__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'])
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'])
return existing.id
record_values = {}
o2m_todo = {} # o2m values are processed after their parent already exists
for field_name, field_value in edi_document.iteritems():
# skip metadata and empty fields
if field_name.startswith('__') or field_value is None or field_value is False:
continue
field_info = self._all_columns.get(field_name)
if not field_info:
_logger.warning('Ignoring unknown field `%s` when importing `%s` EDI document', field_name, self._name)
continue
field = field_info.column
# skip function/related fields
if isinstance(field, fields.function):
_logger.warning("Unexpected function field value found in '%s' EDI document: '%s'" % (self._name, field_name))
continue
relation_model = field._obj
if field._type == 'many2one':
record_values[field_name] = self.edi_import_relation(cr, uid, relation_model,
field_value[1], field_value[0],
context=context)
elif field._type == 'many2many':
record_values[field_name] = [self.edi_import_relation(cr, uid, relation_model, m2m_value[1],
m2m_value[0], context=context)
for m2m_value in field_value]
elif field._type == 'one2many':
# must wait until parent report is imported, as the parent relationship
# is often required in o2m child records
o2m_todo[field_name] = field_value
else:
record_values[field_name] = field_value
module_ref = "%s:%s" % (ext_id_members['module'], ext_id_members['db_uuid'])
record_id = self.pool.get('ir.model.data')._update(cr, uid, self._name, module_ref, record_values,
xml_id=ext_id_members['id'], context=context)
record_display, = self.name_get(cr, uid, [record_id], context=context)
# process o2m values, connecting them to their parent on-the-fly
for o2m_field, o2m_value in o2m_todo.iteritems():
field = self._all_columns[o2m_field].column
dest_model = self.pool.get(field._obj)
for o2m_line in o2m_value:
# link to parent record: expects an (ext_id, name) pair
o2m_line[field._fields_id] = (ext_id_members['full'], record_display[1])
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)
return record_id
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+58
View File
@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2011 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 osv import fields,osv
class res_company(osv.osv):
"""Helper subclass for res.company providing util methods for working with
companies in the context of EDI import/export. The res.company object
itself is not EDI-exportable"""
_inherit = "res.company"
def edi_export_address(self, cr, uid, company, edi_address_struct=None, context=None):
"""Returns a dict representation of the address of the company record, suitable for
inclusion in an EDI document, and matching the given edi_address_struct if provided.
The first found address is returned, in order of preference: invoice, contact, default.
:param browse_record company: company to export
:return: dict containing the address representation for the company record, or
an empty dict if no address can be found
"""
res_partner = self.pool.get('res.partner')
res_partner_address = self.pool.get('res.partner.address')
addresses = res_partner.address_get(cr, uid, [company.partner_id.id], ['default', 'contact', 'invoice'])
addr_id = addresses['invoice'] or addresses['contact'] or addresses['default']
result = {}
if addr_id:
address = res_partner_address.browse(cr, uid, addr_id, context=context)
result = res_partner_address.edi_export(cr, uid, [address], edi_struct=edi_address_struct, context=context)[0]
if company.logo:
result['logo'] = company.logo # already base64-encoded
if company.paypal_account:
result['paypal_account'] = company.paypal_account
# bank info: include only bank account supposed to be displayed in document footers
res_partner_bank = self.pool.get('res.partner.bank')
bank_ids = res_partner_bank.search(cr, uid, [('company_id','=',company.id),('footer','=',True)], context=context)
if bank_ids:
result['bank_ids'] = res_partner_address.edi_m2m(cr, uid,
res_partner_bank.browse(cr, uid, bank_ids, context=context),
context=context)
return result
+64
View File
@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2011 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 osv import fields,osv
from edi import EDIMixin
from openerp import SUPERUSER_ID
RES_CURRENCY_EDI_STRUCT = {
#custom: 'code'
'symbol': True,
'rate': True,
}
class res_currency(osv.osv, EDIMixin):
_inherit = "res.currency"
def edi_export(self, cr, uid, records, edi_struct=None, context=None):
edi_struct = dict(edi_struct or RES_CURRENCY_EDI_STRUCT)
edi_doc_list = []
for currency in records:
# Get EDI doc based on struct. The result will also contain all metadata fields and attachments.
edi_doc = super(res_currency,self).edi_export(cr, uid, [currency], edi_struct, context)[0]
edi_doc.update(code=currency.name)
edi_doc_list.append(edi_doc)
return edi_doc_list
def edi_import(self, cr, uid, edi_document, context=None):
self._edi_requires_attributes(('code','symbol'), edi_document)
external_id = edi_document['__id']
existing_currency = self._edi_get_object_by_external_id(cr, uid, external_id, 'res_currency', context=context)
if existing_currency:
return existing_currency.id
# find with unique ISO code
existing_ids = self.search(cr, uid, [('name','=',edi_document['code'])])
if existing_ids:
return existing_ids[0]
# nothing found, create a new one
currency_id = self.create(cr, SUPERUSER_ID, {'name': edi_document['code'],
'symbol': edi_document['symbol']}, context=context)
rate = edi_document.pop('rate')
if rate:
self.pool.get('res.currency.rate').create(cr, SUPERUSER_ID, {'currency_id': currency_id,
'rate': rate}, context=context)
return currency_id
+104
View File
@@ -0,0 +1,104 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2011 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 osv import fields,osv
from edi import EDIMixin
from openerp import SUPERUSER_ID
from tools.translate import _
RES_PARTNER_ADDRESS_EDI_STRUCT = {
'name': True,
'email': True,
'street': True,
'street2': True,
'zip': True,
'city': True,
'country_id': True,
'state_id': True,
'phone': True,
'fax': True,
'mobile': True,
}
RES_PARTNER_EDI_STRUCT = {
'name': True,
'ref': True,
'lang': True,
'website': True,
'address': RES_PARTNER_ADDRESS_EDI_STRUCT
}
class res_partner(osv.osv, EDIMixin):
_inherit = "res.partner"
def edi_export(self, cr, uid, records, edi_struct=None, context=None):
return super(res_partner,self).edi_export(cr, uid, records,
edi_struct or dict(RES_PARTNER_EDI_STRUCT),
context=context)
class res_partner_address(osv.osv, EDIMixin):
_inherit = "res.partner.address"
def _get_bank_type(self, cr, uid, context=None):
# first option: the "normal" bank type, installed by default
res_partner_bank_type = self.pool.get('res.partner.bank.type')
try:
return self.pool.get('ir.model.data').get_object(cr, uid, 'base', 'bank_normal', context=context).code
except ValueError:
pass
# second option: create a new custom type for EDI or use it if already created, as IBAN type is
# not always appropriate: we need a free-form bank type for max flexibility (users can correct
# data manually after import)
code, label = 'edi_generic', 'Generic Bank Type (auto-created for EDI)'
bank_code_ids = res_partner_bank_type.search(cr, uid, [('code','=',code)], context=context)
if not bank_code_ids:
logging.getLogger('edi.res_partner').info('Normal bank account type is missing, creating '
'a generic bank account type for EDI.')
self.res_partner_bank_type.create(cr, SUPERUSER_ID, {'name': label,
'code': label})
return code
def edi_export(self, cr, uid, records, edi_struct=None, context=None):
return super(res_partner_address,self).edi_export(cr, uid, records,
edi_struct or dict(RES_PARTNER_ADDRESS_EDI_STRUCT),
context=context)
def edi_import(self, cr, uid, edi_document, context=None):
# handle bank info, if any
edi_bank_ids = edi_document.pop('bank_ids', None)
address_id = super(res_partner_address,self).edi_import(cr, uid, edi_document, context=context)
if edi_bank_ids:
address = self.browse(cr, uid, address_id, context=context)
import_ctx = dict((context or {}),
default_partner_id=address.partner_id.id,
default_state=self._get_bank_type(cr, uid, context))
for ext_bank_id, bank_name in edi_bank_ids:
try:
self.edi_import_relation(cr, uid, 'res.partner.bank',
bank_name, ext_bank_id, context=import_ctx)
except osv.except_osv:
# failed to import it, try again with unrestricted default type
logging.getLogger('edi.res_partner').warning('Failed to import bank account using'
'bank type: %s, ignoring', import_ctx['default_state'],
exc_info=True)
return address_id
+3
View File
@@ -0,0 +1,3 @@
"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
+220
View File
@@ -0,0 +1,220 @@
/** 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;
}
.openerp input.invalid {
background-color: #F66 !important;
border: 1px solid #D00 !important;
color: #000;
}
/* following browse-specific hacks need to be separate rules, as browsers are
required to ignore rules with unknown selectors. At time of writing, there
is no standard way to style HTML5 placeholders */
.openerp input.invalid:-moz-placeholder {
color: #FFF;
}
.openerp input.invalid::-webkit-input-placeholder {
color: #FFF;
}
/** 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/ **/
body {
background: #EEE; /* contrast with paper */
}
.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;
}
.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.

After

Width:  |  Height:  |  Size: 6.8 KiB

+175
View File
@@ -0,0 +1,175 @@
openerp.edi = function(openerp) {
openerp.web.qweb.add_template("/web/static/src/xml/base.xml");
openerp.web.qweb.add_template("/edi/static/src/xml/edi.xml");
openerp.web.qweb.add_template("/edi/static/src/xml/edi_account.xml");
openerp.web.qweb.add_template("/edi/static/src/xml/edi_sale_purchase.xml");
openerp.edi = {}
openerp.edi.EdiView = openerp.web.Widget.extend({
init: function(parent, db, token) {
this._super();
this.db = db;
this.token = token;
this.session = new openerp.web.Connection();
this.template = "EdiEmpty";
this.content = "";
this.sidebar = "";
},
start: function() {
this._super();
var param = {"db": this.db, "token": this.token};
this.rpc('/edi/get_edi_document', param, this.on_document_loaded, 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.$element.html(openerp.web.qweb.render("EdiView", param));
this.$element.find('button.oe_edi_action_print').bind('click', this.do_print);
this.$element.find('button#oe_edi_import_existing').bind('click', this.do_import_existing);
this.$element.find('button#oe_edi_import_create').bind('click', this.do_import_create);
this.$element.find('button#oe_edi_download').bind('click', this.do_download);
this.$element.find('.oe_edi_import_choice, .oe_edi_import_choice_label').bind('click', this.toggle_choice('import'));
this.$element.find('.oe_edi_pay_choice, .oe_edi_pay_choice_label').bind('click', this.toggle_choice('pay'));
this.$element.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.$element.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();
}
});
openerp.edi.EdiImport = openerp.web.Widget.extend({
init: function(parent,url) {
this._super();
this.url = url;
var params = {};
this.template = "EdiImport";
this.session = new openerp.web.Connection();
this.login = new openerp.web.Login(this);
this.header = new openerp.web.Header(this);
this.header.on_logout.add(this.login.on_logout);
},
start: function() {
this.session.on_session_invalid.add_last(this.do_ask_login)
this.session.on_session_valid.add_last(this.do_import);
this.header.appendTo($("#oe_header"));
this.session.start();
this.login.appendTo($('#oe_login'));
},
do_ask_login: function() {
this.login.do_ask_login(this.do_import);
},
do_import: function() {
this.rpc('/edi/import_edi_url', {url: this.url}, this.on_imported, this.on_imported_error);
},
on_imported: function(response) {
if ('action' in response) {
this.rpc("/web/session/save_session_action", {the_action: response.action}, function(key) {
window.location = "/web/webclient/home?debug=1&s_action="+encodeURIComponent(key);
});
}
else {
$('<div>').dialog({
modal: true,
title: 'Import Successful!',
buttons: {
Ok: function() {
$(this).dialog("close");
window.location = "/web/webclient/home";
}
}
}).html('The document has been successfully imported!');
}
},
on_imported_error: function(response){
var self = this;
var msg = "Sorry, the document could not be imported.";
if (response.data.fault_code) {
msg += "\n Reason:" + response.data.fault_code;
}
var params = {error: response, message: msg};
$(openerp.web.qweb.render("DialogWarning", params)).dialog({
title: "Document Import Notification",
modal: true,
buttons: {
Ok: function() { $(this).dialog("close"); }
}
});
}
});
}
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
+93
View File
@@ -0,0 +1,93 @@
<template>
<t t-name="EdiEmpty">
<div style="height:100%;"></div>
</t>
<t t-name="EdiImport">
<t t-call="Interface"/>
</t>
<t t-name="EdiView">
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%" id="oe_app" class="oe-application oe_forms">
<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: 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_form_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">
<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>
+163
View File
@@ -0,0 +1,163 @@
<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="_.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-esc="doc.name"/></td>
<td align="left"><t t-esc="doc.date_invoice"/></td>
<td align="left"><t 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="_.sprintf('%.2f',invoice_line.quantity)"/> <t t-esc="invoice_line.uos_id[1]"/></td>
<td align="right"><t t-esc="_.sprintf('%.2f',invoice_line.price_unit)"/></td>
<td align="right"><t t-esc="_.sprintf('%.2f',invoice_line.discount)"/></td>
<td align="right"><t t-esc="_.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="_.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="_.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="_.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="_.sprintf('%.2f',tax_line.base_amount)"/> <t t-esc="doc.currency.code"/></td>
<td align="right"><t t-esc="_.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="_.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>
@@ -0,0 +1,161 @@
<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>
<h1 class="oe_edi_doc_title">Order <t t-esc="doc.name"/>: <t t-esc="_.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">Your Reference</th>
<th align="left">Date</th>
<th align="left">Salesman</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="_.sprintf('%.2f',doc_line.product_qty)"/> <t t-esc="doc_line.product_uom[1]"/>
</td>
<td align="right"><t t-esc="_.sprintf('%.2f',doc_line.price_unit)"/></td>
<td align="right"><t t-esc="_.sprintf('%.2f',doc_line.discount or 0.0)"/></td>
<td align="right"><t t-esc="_.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="_.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="_.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="_.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 &amp;&amp; (doc.order_policy == 'prepaid' || doc.order_policy == 'manual')">
<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="_.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>
+44
View File
@@ -0,0 +1,44 @@
-
In order to test the basic EDI system, I will export a partner,
modify the exported EDI document to add an attachment and change
the data, and then re-import it and re-export it.
with an attached file, check the result, the alter the data
and reimport it.
-
!python {model: edi.document}: |
import json
partner_obj = self.pool.get('res.partner')
tokens = self.export_edi(cr, uid, [partner_obj.browse(cr, uid, ref('base.res_partner_agrolait'))])
doc = self.get_document(cr, uid, tokens[0], context=context)
edi_doc, = json.loads(doc)
# check content of the document
assert edi_doc.get('__id').endswith('.res_partner_agrolait'), 'Incorrect external ID'
assert edi_doc.get('__model') == 'res.partner', 'Incorrect/Missing __model'
assert edi_doc.get('__module') == 'base', 'Incorrect/Missing __module'
assert edi_doc.get('__last_update'), 'Missing __last_update'
# try to import the document after changing the name and id, and attaching a
# file, and check that a new partner is returned
edi_doc['__id'] = 'base:xxd37f8a-xx55-11e0-xxdd-xx81124c8b50.res_partner_xxx'
edi_doc['name'] = 'AgroMilk'
attachment = {
'name': 'Test file',
'file_name': 'test.png',
# base64 standard requires blocks of 57bytes=76chars, NL-separated
'content': 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAAKCAYAAACE2W/HAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A\n/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9oIDQ84BkjLWAYAAACNSURBVCjP\njZKxDUJBDEOf/wbUbEHDhkxAzRjUDECBhMQYjIApuPBNpK+PpUinOPYll5Nt1iCJXifbSPpJZtHg\nXLUdu0FWpMEt87a/xtsmqjjNetPFyhuWYFuj7RcgQBNwXNGdw2CqY85yXWi5z7YBnmRyEHvg0YSX\nrLE9P30nwugOHHr+s5va4x+fofAGm1+JjnJICm0AAAAASUVORK5CYII=\n',
}
edi_doc['__attachments'] = [attachment]
doc = json.dumps([edi_doc])
result, = self.import_edi(cr, uid, edi_document=doc)
assert result[0] == 'res.partner' and result[1] > ref('base.res_partner_agrolait'),\
"Expected (%r,> %r) after import 1, got %r" % ('res.partner', ref('base.res_partner_agrolait'), 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)
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), \
'Incorrect value for %s, expected %r, got %r' % (attribute, edi_doc.get(attribute), edi_doc_output.get(attribute))
+1
View File
@@ -21,5 +21,6 @@
##############################################################################
import email_template
import wizard
import res_partner
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+4
View File
@@ -61,9 +61,13 @@ Openlabs was kept
"data": [
'wizard/email_template_preview_view.xml',
'email_template_view.xml',
'res_partner_view.xml',
'wizard/email_compose_message_view.xml',
'security/ir.model.access.csv'
],
"demo": [
'res_partner_demo.yml',
],
"installable": True,
"active": False,
"certificate" : "00817073628967384349",
+18 -5
View File
@@ -28,6 +28,7 @@ from osv import osv
from osv import fields
import tools
from tools.translate import _
from urllib import quote as quote
try:
from mako.template import Template as MakoTemplate
@@ -66,6 +67,7 @@ class email_template(osv.osv):
user=user,
# context kw would clash with mako internals
ctx=context,
quote=quote,
format_exceptions=True)
if result == u'False':
result = u''
@@ -305,6 +307,7 @@ class email_template(osv.osv):
'attachment_ids': False,
'message_id': False,
'state': 'outgoing',
'subtype': 'plain',
}
if not template_id:
return values
@@ -319,6 +322,9 @@ class email_template(osv.osv):
template.model, res_id, context=context) \
or False
if values['body_html']:
values.update(subtype='html')
if template.user_signature:
signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature
values['body_text'] += '\n\n' + signature
@@ -355,20 +361,24 @@ class email_template(osv.osv):
values['attachments'] = attachments
return values
def send_mail(self, cr, uid, template_id, res_id, context=None):
def send_mail(self, cr, uid, template_id, res_id, force_send=False, context=None):
"""Generates a new mail message for the given template and record,
and schedule it for delivery through the ``mail`` module's scheduler.
and schedules it for delivery through the ``mail`` module's scheduler.
:param int template_id: id of the template to render
:param int res_id: id of the record to render the template with
(model is taken from the template)
:param bool force_send: if True, the generated mail.message is
immediately sent after being created, as if the scheduler
was executed for this message only.
:returns: id of the mail.message that was created
"""
mail_message = self.pool.get('mail.message')
ir_attachment = self.pool.get('ir.attachment')
template = self.browse(cr, uid, template_id, context)
values = self.generate_email(cr, uid, template_id, res_id, context=context)
attachments = values.pop('attachments') or {}
message_id = mail_message.create(cr, uid, values, context=context)
msg_id = mail_message.create(cr, uid, values, context=context)
# link attachments
attachment_ids = []
for fname, fcontent in attachments.iteritems():
@@ -377,10 +387,13 @@ class email_template(osv.osv):
'datas_fname': fname,
'datas': fcontent,
'res_model': mail_message._name,
'res_id': message_id,
'res_id': msg_id,
}
if context.has_key('default_type'):
del context['default_type']
attachment_ids.append(ir_attachment.create(cr, uid, attachment_data, context))
attachment_ids.append(ir_attachment.create(cr, uid, attachment_data, context=context))
if force_send:
mail_message.send(cr, uid, [msg_id], context=context)
return msg_id
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -155,9 +155,8 @@
<field name="search_view_id" ref="view_email_template_search"/>
</record>
<menuitem name="Templates" id="menu_email_template_all_tools"
parent="mail.menu_email_message_tools" action="action_email_template_tree_all" />
<menuitem id="menu_email_templates" parent="base.menu_email" action="action_email_template_tree_all"
sequence="20"/>
</data>
</openerp>
+37
View File
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2011 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 osv import fields,osv
class res_partner(osv.osv):
"""Inherit res.partner to add a generic opt-out field that can be used
to restrict usage of automatic email templates.
This field is unused by default. """
_inherit = 'res.partner'
_columns = {
'opt_out': fields.boolean('Opt-out', help="If checked, this partner will not receive any automated email " \
"notifications, such as the availability of invoices."),
}
_defaults = {
'opt_out': False,
}
@@ -0,0 +1,9 @@
-
Set opt-out to True on all demo partners
-
!python {model: res.partner}: |
partner_ids = self.search(cr, uid, [])
# assume partners with an external ID come from demo data
ext_ids = self._get_external_ids(cr, uid, partner_ids)
ids_to_update = [k for (k,v) in ext_ids.iteritems() if v]
self.write(cr, uid, ids_to_update, {'opt_out': True})
@@ -0,0 +1,16 @@
<?xml version="1.0" ?>
<openerp>
<data>
<record model="ir.ui.view" id="res_partner_opt_out_form">
<field name="name">res.partner.opt_out.form</field>
<field name="model">res.partner</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<xpath expr="/form/notebook/page/field[@name='active']" position="after">
<field name="opt_out" groups="base.group_extended"/>
</xpath>
</field>
</record>
</data>
</openerp>

Some files were not shown because too many files have changed in this diff Show More