[Merge]with: lp:openobject-addons
bzr revid: dbr@tinyerp.com-20111117064746-mue10buy3i62rio0
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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
@@ -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([
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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','<>','view'),('type','<>','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','<>','view'),('type','<>','consolidation')]"/>
|
||||
<field name="account_id" domain="[('type','<>','view'), ('type','<>','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>
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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;">
|
||||
<strong>REFERENCES</strong><br />
|
||||
Invoice number: <strong>${object.number}</strong><br />
|
||||
Invoice total: <strong>${object.amount_total} ${object.currency_id.name}</strong><br />
|
||||
Invoice date: ${object.date_invoice}<br />
|
||||
% if object.origin:
|
||||
Order reference: ${object.origin}<br />
|
||||
% endif
|
||||
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&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)
|
||||
%>
|
||||
<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: ${object.company_id.phone}
|
||||
</div>
|
||||
% endif
|
||||
% if object.company_id.website:
|
||||
<div>
|
||||
Web : <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¤cy_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
File diff suppressed because it is too large
Load Diff
@@ -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'))" />
|
||||
|
||||
+1
-2
@@ -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
|
||||
-
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -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'))" />
|
||||
|
||||
@@ -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','<=',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="['|', '&', ('date', '!=', False), ('date', '<', time.strftime('%%Y-%%m-%%d')), ('is_overdue_quantity', '=', True)]"
|
||||
<filter icon="terp-go-today" string="To Renew" domain="['|', '&', ('date', '!=', False), ('date', '<=', 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>
|
||||
|
||||
@@ -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','<=', time.strftime('%%Y-%%m-%%d')),('purchase_date','>=',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','<=',(datetime.date.today()+relativedelta(day=31)).strftime('%%Y-%%m-%%d')),('purchase_date','>=',(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','<=', (datetime.date.today() - relativedelta(day=31, months=1)).strftime('%%Y-%%m-%%d')),('purchase_date','>=',(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'))" />
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)])
|
||||
|
||||
@@ -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)])
|
||||
|
||||
@@ -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)])
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
-
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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),
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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):
|
||||
|
||||
@@ -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','<=', time.strftime('%%Y-%%m-%%d')),('create_date','>=',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','<=',(datetime.date.today()+relativedelta(day=31)).strftime('%%Y-%%m-%%d')),('create_date','>=',(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','<=', (datetime.date.today() - relativedelta(day=31, months=1)).strftime('%%Y-%%m-%%d')),('create_date','>=',(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','<=', time.strftime('%%Y-%%m-%%d')),('create_date','>=',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','<=',(datetime.date.today()+relativedelta(day=31)).strftime('%%Y-%%m-%%d')),('create_date','>=',(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','<=', (datetime.date.today() - relativedelta(day=31, months=1)).strftime('%%Y-%%m-%%d')),('create_date','>=',(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','<=', time.strftime('%%Y-%%m-%%d')),('create_date','>=',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','<=',(datetime.date.today()+relativedelta(day=31)).strftime('%%Y-%%m-%%d')),('create_date','>=',(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','<=', (datetime.date.today() - relativedelta(day=31, months=1)).strftime('%%Y-%%m-%%d')),('create_date','>=',(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>
|
||||
|
||||
@@ -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"""
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
|
||||
@@ -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:
|
||||
@@ -0,0 +1 @@
|
||||
import main
|
||||
@@ -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
|
||||
|
||||
#
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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:
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
@@ -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 |
@@ -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 +
|
||||
"¤cy_code=" + cur_code +
|
||||
"&button_subtype=services&no_note=1&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:
|
||||
@@ -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+'&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>
|
||||
@@ -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 && (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 && (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>
|
||||
@@ -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))
|
||||
@@ -21,5 +21,6 @@
|
||||
##############################################################################
|
||||
import email_template
|
||||
import wizard
|
||||
import res_partner
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user