From 29532c645821c5f1d66d51a28d75102b1207a932 Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Thu, 16 Jun 2011 11:37:31 +0530 Subject: [PATCH 001/132] [EDI] first revision of specific import bzr revid: aar@tinyerp.com-20110616060731-rmp0eftd3hjlm3ep --- addons/account/__init__.py | 2 +- addons/account/__openerp__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/account/__init__.py b/addons/account/__init__.py index 24129306e13..799f6fe08a5 100644 --- a/addons/account/__init__.py +++ b/addons/account/__init__.py @@ -34,5 +34,5 @@ import product import sequence import company import res_currency - +import edi_invoice # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index e4059896783..039a6800a98 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -143,7 +143,7 @@ module named account_voucher. 'test/account_bank_statement.yml', 'test/account_cash_statement.yml', 'test/account_report.yml', - + 'test/test_edi_invoice.yml', ], 'installable': True, From e26ae4e800d05e3e3045972cb234847c4e11ef82 Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Tue, 21 Jun 2011 18:59:25 +0530 Subject: [PATCH 002/132] [EDI] refactor code of specific import bzr revid: aar@tinyerp.com-20110621132925-95jijdbuml3st9bo --- addons/account/__openerp__.py | 6 ++++-- addons/account/wizard/__init__.py | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index 039a6800a98..07174e4971f 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -121,7 +121,9 @@ module named account_voucher. 'company_view.xml', 'board_account_view.xml', "wizard/account_report_profit_loss_view.xml", - "wizard/account_report_balance_sheet_view.xml" + "wizard/account_report_balance_sheet_view.xml", + 'test/test_edi_invoice.yml', + #'wizard/edi_import_view.xml', ], 'demo_xml': [ 'account_demo.xml', @@ -143,7 +145,7 @@ module named account_voucher. 'test/account_bank_statement.yml', 'test/account_cash_statement.yml', 'test/account_report.yml', - 'test/test_edi_invoice.yml', + ], 'installable': True, diff --git a/addons/account/wizard/__init__.py b/addons/account/wizard/__init__.py index caed6fc3977..0d888f8e712 100644 --- a/addons/account/wizard/__init__.py +++ b/addons/account/wizard/__init__.py @@ -65,6 +65,7 @@ import account_change_currency import account_report_balance_sheet import account_report_profit_loss + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From df0bbd0650db82a82711e601128d9b2a4b6ebfe1 Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Tue, 21 Jun 2011 19:00:26 +0530 Subject: [PATCH 003/132] [EDI] refactor code of specific import bzr revid: aar@tinyerp.com-20110621133026-ll3afhk9qhwx52is --- addons/account/edi_invoice.py | 287 +++++++++++++++++++++++ addons/account/test/test_edi_invoice.yml | 13 + 2 files changed, 300 insertions(+) create mode 100644 addons/account/edi_invoice.py create mode 100644 addons/account/test/test_edi_invoice.yml diff --git a/addons/account/edi_invoice.py b/addons/account/edi_invoice.py new file mode 100644 index 00000000000..956b0d9aa15 --- /dev/null +++ b/addons/account/edi_invoice.py @@ -0,0 +1,287 @@ + +from osv import fields, osv,orm +from base.ir import ir_edi + +class account_invoice(osv.osv,ir_edi.edi): + + _inherit = 'account.invoice' + #_name = 'edi.invoice' + + def edi_export(self, cr, uid, ids, edi_struct=None, context=None): + """Exports a supplier or customer invoice""" + rec_id = [ids[0].id] + + 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, + 'address_invoice_id': True, #only one address needed + 'payment_term': True, + 'currency_id': True, + 'invoice_line': { + 'name': True, + 'origin': True, + 'uos_id': True, + 'product_id': True, + 'price_unit': True, + 'quantity': True, + 'discount': True, + 'note': True, + }, + 'tax_line': { + 'name': True, + 'base': True, + 'amount': True, + 'manual': True, + 'sequence': True, + 'base_amount': True, + 'tax_amount': True, + }, + } + # Get EDI doc based on this struct. The result will also contain + # all metadata fields and attachments. + + edi_doc = super(account_invoice,self).edi_export(cr, uid, ids, edi_struct, context) + + + for i, invoice in enumerate(self.browse(cr, uid, rec_id, context=context)): + # add specific data for import + inv_comp = invoice.company_id + + + comp_partner = inv_comp.partner_id + + comp_partner_addr = comp_partner.address + + for address in comp_partner_addr: + + + edi_doc[i].update({ + # Add company info and address + 'company_address': { + 'street': address.street, + 'street2': address.street2, + 'zip': address.zip, + 'city': address.city, + 'state_id': self.edi_m2o(cr, uid, address.state_id), + 'country_id': self.edi_m2o(cr, uid, address.country_id), + 'email': address.email, + 'phone': address.phone + }, + # Function fields are not included in normal export + #'company_logo': inv_comp.logo,#TODO + #'paid': inv_comp.paid, #TODO + }) + + return edi_doc + 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. + """ + # generic implementation! + + partner = self.pool.get('res.partner') + partner_add = self.pool.get('res.partner.address') + model_data = self.pool.get('ir.model.data') + product_obj = self.pool.get('product.product') + product_categ = self.pool.get('product.category') + acc_invoice = self.pool.get('account.invoice') + company = self.pool.get('res.company') + tax_id = [] + account_id = [] + partner_id = None + company_id = None + if context is None: + context = {} + for field in edi_document.keys(): + + if field == 'type': + if len(edi_document['invoice_line']): + name = edi_document['invoice_line'][0]['product_id'][1] + else: + name = None + re_ids = product_obj.search(cr,uid,[('name','=',name)]) + if edi_document['type'] == 'out_invoice' or edi_document['type'] == 'out_refund': + if re_ids: + if product_obj.browse(cr,uid,re_ids)[0].property_account_expense: + account_id = product_obj.browse(cr,uid,re_ids)[0].property_account_expense + + else: + account_id = product_categ.browse(cr,uid,re_ids)[0].property_account_expense_categ + if product_obj.browse(cr,uid,re_ids)[0].taxes_id: + tax_id = product_obj.browse(cr, uid,re_ids)[0].taxes_id + if edi_document['type'] == 'out_refund': + edi_document['type'] = 'in_refund' + else: + edi_document['type'] = 'in_invoice' + + + elif edi_document['type'] == 'in_invoice' or edi_document['type'] == 'in_refund': + if re_ids: + if product_obj.browse(cr,uid,re_ids)[0].property_account_income: + account_id = product_obj.browse(cr,uid,re_ids)[0].property_account_income + else: + account_id = product_categ.browse(cr,uid,re_ids)[0].property_account_income_categ + if product_obj.browse(cr,uid,re_ids)[0].taxes_id: + tax_id = product_obj.browse(cr, uid,re_ids)[0].taxes_id + if edi_document['type'] == 'in_refund': + edi_document['type'] = 'out_refund' + else: + edi_document['type'] = 'out_invoice' + + if account_id: + name_ids = model_data.search(cr, uid, [('model','=',account_id._name),('res_id','=',account_id.id)]) + if name_ids: + xml_id = model_data.browse(cr, uid, name_ids)[0].name + db_uuid = ir_edi.safe_unique_id(account_id._name,account_id.id) + edi_document['invoice_line'][0]['account_id'] = [db_uuid+':'+xml_id,account_id.name] + + if tax_id: + name_ids = model_data.search(cr, uid, [('model','=',tax_id[0]._name),('res_id','=',tax_id[0].id)]) + + if name_ids: + xml_id = model_data.browse(cr, uid, name_ids)[0].name + db_uuid = ir_edi.safe_unique_id(tax_id[0]._name,tax_id[0].id) + edi_document['tax_line'][0]['account_id'] = [db_uuid+':'+xml_id,tax_id[0].name] + + else: + if len(edi_document['tax_line']): + edi_document['tax_line'][0]['manual'] = True + + res = {} + part = {} + comp = {} + partner_id = partner.search(cr,uid,[('name','=',edi_document['company_id'][1])]) + + if len(partner_id): + + browse_partner = partner.browse(cr,uid,partner_id[0]) + u_id = model_data.search(cr, uid, [('res_id','=',browse_partner.id),('model','=',browse_partner._name)]) + if len(u_id): + company_id = browse_partner.company_id + xml_obj = model_data.browse(cr,uid,u_id[0]) + uuid = ir_edi.safe_unique_id(browse_partner._name,browse_partner.id) + db_uuid = '%s:%s' % (uuid,xml_obj.name) + part.update({'partner_id':[db_uuid,browse_partner.name]}) + + else: + company_id = company.create(cr, uid, {'name':edi_document['company_id'][1]}) + add_id = partner_add.create(cr,uid,edi_document['company_address']) + res.update({'name': edi_document['company_id'][1],'supplier': True, 'partner_id': edi_document['partner_id'],'address':add_id}) + partner_id = partner.create(cr,uid,res) + + browse_partner = partner.browse(cr,uid,partner_id[0]) + + u_id = model_data.search(cr, uid, [('res_id','=',browse_partner.id),('model','=',browse_partner._name)]) + + + if len(u_id): + + xml_obj = model_data.browse(cr,uid,u_id[0]) + uuid = ir_edi.safe_unique_id(browse_partner._name,browse_partner.id) + db_uuid = '%s:%s' % (uuid,xml_obj.name) + part.update({'partner_id':[db_uuid,browse_partner.name]}) + + comp_id = partner.search(cr,uid,[('name','=',edi_document['partner_id'][1])]) + + if len(comp_id): + + browse_partner = partner.browse(cr,uid,comp_id[0]) + + browse_company = company.browse(cr,uid,browse_partner.company_id.id) + + u_id = u_id = model_data.search(cr, uid, [('res_id','=',browse_company.id),('model','=',browse_company._name)]) + if len(u_id): + + xml_obj = model_data.browse(cr,uid,u_id[0]) + uuid = ir_edi.safe_unique_id(browse_company._name,browse_company.id) + db_uuid = '%s:%s' % (uuid,xml_obj.name) + comp.update({'company_id':[db_uuid,browse_company.name]}) + + del edi_document['partner_id'] + del edi_document['company_id'] + edi_document.update(part) + edi_document.update(comp) + if len(partner_id): + + + + p = self.pool.get('res.partner').browse(cr, uid, partner_id[0]) + if company_id: + if p.property_account_receivable.company_id.id != company_id.id and p.property_account_payable.company_id.id != company_id.id: + property_obj = self.pool.get('ir.property') + + rec_pro_id = property_obj.search(cr,uid,[('name','=','property_account_receivable'),('res_id','=','res.partner,'+str(partner_id)+''),('company_id','=',company_id.id)]) + pay_pro_id = property_obj.search(cr,uid,[('name','=','property_account_payable'),('res_id','=','res.partner,'+str(partner_id)+''),('company_id','=',company_id)]) + if not rec_pro_id: + rec_pro_id = property_obj.search(cr,uid,[('name','=','property_account_receivable'),('company_id','=',company_id.id)]) + if not pay_pro_id: + pay_pro_id = property_obj.search(cr,uid,[('name','=','property_account_payable'),('company_id','=',company_id.id)]) + rec_line_data = property_obj.read(cr,uid,rec_pro_id,['name','value_reference','res_id']) + pay_line_data = property_obj.read(cr,uid,pay_pro_id,['name','value_reference','res_id']) + rec_res_id = rec_line_data and rec_line_data[0].get('value_reference',False) and int(rec_line_data[0]['value_reference'].split(',')[1]) or False + pay_res_id = pay_line_data and pay_line_data[0].get('value_reference',False) and int(pay_line_data[0]['value_reference'].split(',')[1]) or False + if not rec_res_id and not pay_res_id: + raise osv.except_osv(_('Configuration Error !'), + _('Can not find account chart for this company, Please Create account.')) + account_obj = self.pool.get('account.account') + rec_obj_acc = account_obj.browse(cr, uid, [rec_res_id]) + pay_obj_acc = account_obj.browse(cr, uid, [pay_res_id]) + p.property_account_receivable = rec_obj_acc[0] + p.property_account_payable = pay_obj_acc[0] + + if edi_document['type'] in ('out_invoice', 'out_refund'): + acc_obj = p.property_account_receivable + else: + acc_obj = p.property_account_payable + res_id = model_data.search(cr,uid,[('model','=',acc_obj._name),('res_id','=',acc_obj.id)]) + if len(res_id): + xml_obj = model_data.browse(cr, uid, res_id[0]) + uuid = ir_edi.safe_unique_id(acc_obj._name,acc_obj.id) + db_uuid = '%s:%s' % (uuid,xml_obj.name) + edi_document.update({'account_id':[db_uuid,acc_obj.name]}) + print "account_id",edi_document['account_id'] + edi_document.update({'reference':edi_document['internal_number'],'reference_type' : 'none'}) + edi_document['internal_number'] = False + context['type'] = edi_document['type'] + comp + del edi_document['company_address'] + + + return super(account_invoice,self).edi_import(cr, uid, edi_document) + +account_invoice() + +class account_invoice_line(osv.osv, ir_edi.edi): + _inherit='account.invoice.line' + +account_invoice_line() +class account_invoice_tax(osv.osv, ir_edi.edi): + _inherit = "account.invoice.tax" + +account_invoice_tax() diff --git a/addons/account/test/test_edi_invoice.yml b/addons/account/test/test_edi_invoice.yml new file mode 100644 index 00000000000..af23dd3e57a --- /dev/null +++ b/addons/account/test/test_edi_invoice.yml @@ -0,0 +1,13 @@ +- + Tesing of Export functionality +- + !python {model: ir.edi.document}: | + invoice_obj = self.pool.get('account.invoice') + invoice_ids = invoice_obj.search(cr, uid, []) + print "invoices>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",invoice_ids + if invoice_ids: + invoices = invoice_obj.browse(cr, uid, invoice_ids) + tokens = self.export_edi(cr, uid, invoices) + for token in tokens: + document = self.get_document(cr, uid, token, context=context) + a = self.import_edi(cr, uid, edi_document = document) From 7f241bd2e7db48a791a65bc0ce83fd2d7cbf34ca Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Wed, 22 Jun 2011 14:44:25 +0530 Subject: [PATCH 004/132] [EDI] refactor code of specific edi import and export bzr revid: aar@tinyerp.com-20110622091425-b1ldh2oduyglaoa1 --- addons/account/__openerp__.py | 13 ++++++------ addons/account/edi_invoice.py | 39 ++++------------------------------- 2 files changed, 10 insertions(+), 42 deletions(-) diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index 07174e4971f..b2996976c50 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -122,14 +122,13 @@ module named account_voucher. 'board_account_view.xml', "wizard/account_report_profit_loss_view.xml", "wizard/account_report_balance_sheet_view.xml", - 'test/test_edi_invoice.yml', - #'wizard/edi_import_view.xml', + ], 'demo_xml': [ - 'account_demo.xml', - 'project/project_demo.xml', - 'project/analytic_account_demo.xml', - 'demo/account_minimal.xml', + #'account_demo.xml', + #'project/project_demo.xml', + #'project/analytic_account_demo.xml', + #'demo/account_minimal.xml', # 'account_unit_test.xml', ], 'test': [ @@ -145,7 +144,7 @@ module named account_voucher. 'test/account_bank_statement.yml', 'test/account_cash_statement.yml', 'test/account_report.yml', - + 'test/test_edi_invoice.yml', ], 'installable': True, diff --git a/addons/account/edi_invoice.py b/addons/account/edi_invoice.py index 956b0d9aa15..56d0e52df95 100644 --- a/addons/account/edi_invoice.py +++ b/addons/account/edi_invoice.py @@ -47,21 +47,13 @@ class account_invoice(osv.osv,ir_edi.edi): # Get EDI doc based on this struct. The result will also contain # all metadata fields and attachments. - edi_doc = super(account_invoice,self).edi_export(cr, uid, ids, edi_struct, context) - - + edi_doc = super(account_invoice,self).edi_export(cr, uid, ids, edi_struct, context) for i, invoice in enumerate(self.browse(cr, uid, rec_id, context=context)): # add specific data for import inv_comp = invoice.company_id - - comp_partner = inv_comp.partner_id - comp_partner_addr = comp_partner.address - for address in comp_partner_addr: - - edi_doc[i].update({ # Add company info and address 'company_address': { @@ -119,7 +111,6 @@ class account_invoice(osv.osv,ir_edi.edi): if context is None: context = {} for field in edi_document.keys(): - if field == 'type': if len(edi_document['invoice_line']): name = edi_document['invoice_line'][0]['product_id'][1] @@ -130,7 +121,6 @@ class account_invoice(osv.osv,ir_edi.edi): if re_ids: if product_obj.browse(cr,uid,re_ids)[0].property_account_expense: account_id = product_obj.browse(cr,uid,re_ids)[0].property_account_expense - else: account_id = product_categ.browse(cr,uid,re_ids)[0].property_account_expense_categ if product_obj.browse(cr,uid,re_ids)[0].taxes_id: @@ -138,9 +128,7 @@ class account_invoice(osv.osv,ir_edi.edi): if edi_document['type'] == 'out_refund': edi_document['type'] = 'in_refund' else: - edi_document['type'] = 'in_invoice' - - + edi_document['type'] = 'in_invoice' elif edi_document['type'] == 'in_invoice' or edi_document['type'] == 'in_refund': if re_ids: if product_obj.browse(cr,uid,re_ids)[0].property_account_income: @@ -163,12 +151,10 @@ class account_invoice(osv.osv,ir_edi.edi): if tax_id: name_ids = model_data.search(cr, uid, [('model','=',tax_id[0]._name),('res_id','=',tax_id[0].id)]) - if name_ids: xml_id = model_data.browse(cr, uid, name_ids)[0].name db_uuid = ir_edi.safe_unique_id(tax_id[0]._name,tax_id[0].id) edi_document['tax_line'][0]['account_id'] = [db_uuid+':'+xml_id,tax_id[0].name] - else: if len(edi_document['tax_line']): edi_document['tax_line'][0]['manual'] = True @@ -177,9 +163,7 @@ class account_invoice(osv.osv,ir_edi.edi): part = {} comp = {} partner_id = partner.search(cr,uid,[('name','=',edi_document['company_id'][1])]) - if len(partner_id): - browse_partner = partner.browse(cr,uid,partner_id[0]) u_id = model_data.search(cr, uid, [('res_id','=',browse_partner.id),('model','=',browse_partner._name)]) if len(u_id): @@ -188,20 +172,14 @@ class account_invoice(osv.osv,ir_edi.edi): uuid = ir_edi.safe_unique_id(browse_partner._name,browse_partner.id) db_uuid = '%s:%s' % (uuid,xml_obj.name) part.update({'partner_id':[db_uuid,browse_partner.name]}) - else: company_id = company.create(cr, uid, {'name':edi_document['company_id'][1]}) add_id = partner_add.create(cr,uid,edi_document['company_address']) res.update({'name': edi_document['company_id'][1],'supplier': True, 'partner_id': edi_document['partner_id'],'address':add_id}) partner_id = partner.create(cr,uid,res) - browse_partner = partner.browse(cr,uid,partner_id[0]) - u_id = model_data.search(cr, uid, [('res_id','=',browse_partner.id),('model','=',browse_partner._name)]) - - if len(u_id): - xml_obj = model_data.browse(cr,uid,u_id[0]) uuid = ir_edi.safe_unique_id(browse_partner._name,browse_partner.id) db_uuid = '%s:%s' % (uuid,xml_obj.name) @@ -210,14 +188,10 @@ class account_invoice(osv.osv,ir_edi.edi): comp_id = partner.search(cr,uid,[('name','=',edi_document['partner_id'][1])]) if len(comp_id): - browse_partner = partner.browse(cr,uid,comp_id[0]) - browse_company = company.browse(cr,uid,browse_partner.company_id.id) - u_id = u_id = model_data.search(cr, uid, [('res_id','=',browse_company.id),('model','=',browse_company._name)]) if len(u_id): - xml_obj = model_data.browse(cr,uid,u_id[0]) uuid = ir_edi.safe_unique_id(browse_company._name,browse_company.id) db_uuid = '%s:%s' % (uuid,xml_obj.name) @@ -228,9 +202,6 @@ class account_invoice(osv.osv,ir_edi.edi): edi_document.update(part) edi_document.update(comp) if len(partner_id): - - - p = self.pool.get('res.partner').browse(cr, uid, partner_id[0]) if company_id: if p.property_account_receivable.company_id.id != company_id.id and p.property_account_payable.company_id.id != company_id.id: @@ -265,14 +236,12 @@ class account_invoice(osv.osv,ir_edi.edi): uuid = ir_edi.safe_unique_id(acc_obj._name,acc_obj.id) db_uuid = '%s:%s' % (uuid,xml_obj.name) edi_document.update({'account_id':[db_uuid,acc_obj.name]}) - print "account_id",edi_document['account_id'] + edi_document.update({'reference':edi_document['internal_number'],'reference_type' : 'none'}) edi_document['internal_number'] = False context['type'] = edi_document['type'] - comp + del edi_document['company_address'] - - return super(account_invoice,self).edi_import(cr, uid, edi_document) account_invoice() From 1d98e87dcca53ad9b89007d602734ec1b629821f Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Wed, 22 Jun 2011 15:44:47 +0530 Subject: [PATCH 005/132] [EDI] revised code of specific import and export bzr revid: aar@tinyerp.com-20110622101447-qm824uxvcedyjgd4 --- addons/account/__openerp__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index b2996976c50..651c426dc80 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -125,10 +125,10 @@ module named account_voucher. ], 'demo_xml': [ - #'account_demo.xml', - #'project/project_demo.xml', - #'project/analytic_account_demo.xml', - #'demo/account_minimal.xml', + 'account_demo.xml', + 'project/project_demo.xml', + 'project/analytic_account_demo.xml', + 'demo/account_minimal.xml', # 'account_unit_test.xml', ], 'test': [ From 77b2a20d81980235353b5e33f7b8897bfbe355b9 Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Thu, 23 Jun 2011 19:12:07 +0530 Subject: [PATCH 006/132] [EDI]:customer invoice testing through yml bzr revid: aar@tinyerp.com-20110623134207-soeh9tgaknfhp6hf --- addons/account/__openerp__.py | 3 +- addons/account/test/test_edi_invoice.yml | 64 ++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index 651c426dc80..1bb1b56a476 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -122,8 +122,7 @@ module named account_voucher. 'board_account_view.xml', "wizard/account_report_profit_loss_view.xml", "wizard/account_report_balance_sheet_view.xml", - - ], + ], 'demo_xml': [ 'account_demo.xml', 'project/project_demo.xml', diff --git a/addons/account/test/test_edi_invoice.yml b/addons/account/test/test_edi_invoice.yml index af23dd3e57a..9d624489c8c 100644 --- a/addons/account/test/test_edi_invoice.yml +++ b/addons/account/test/test_edi_invoice.yml @@ -1,13 +1,71 @@ - - Tesing of Export functionality + create Company +- + !record {model: res.company, id: res_company_test}: + name: Thomson pvt. ltd. + partner_id: 1 + rml_header: 1 + rml_header2: 1 + rml_header3: 1 + currency_id: 1 + +- + create customer +- + !record {model: res.partner, id: res_partner_test}: + name: Junjun wala + supplier: False + company_id: res_company_test + address: + - partner_id: res_partner_test + type: default + name: ivan + street: m g road + street2: line 28 + city: paris + +- + Creating a customer invoice record +- + !record {model: account.invoice, id: account_invoice_test}: + amount_tax: 0.0 + amount_total: 83.0 + amount_untaxed: 83.0 + currency_id: base.EUR + date_invoice: '2011-06-22' + invoice_line: + name: '[CPU1] Processor AMD Athlon XP 1800+' + partner_id: res_partner_test + price_subtotal: 75.0 + price_unit: 75.0 + product_id: product.product_product_cpu1 + quantity: 1.0 + journal_id: 'False' + name: Nothing + partner_id: res_partner_test + payment_term: account.account_payment_term + reference_type: none + +- + Tesing of Export functionality and Import Functionality - !python {model: ir.edi.document}: | + invoice_obj = self.pool.get('account.invoice') - invoice_ids = invoice_obj.search(cr, uid, []) - print "invoices>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",invoice_ids + invoice_ids = invoice_obj.search(cr, uid, [partner_id]) if invoice_ids: invoices = invoice_obj.browse(cr, uid, invoice_ids) tokens = self.export_edi(cr, uid, invoices) for token in tokens: document = self.get_document(cr, uid, token, context=context) a = self.import_edi(cr, uid, edi_document = document) + +- + Check after import of document customer become supplier or not +- + !python {model: account.invoice}: | + ids=self.search(cr, uid, [('partner_id','=',base.main_company),('company_id','=',res_company_test),('type','=','in_invoice')]) + if not ids: + raise AssertionError("Invoice is not imported") + + \ No newline at end of file From 66356f01af42f48df61064ae1fc5ff1f0453de92 Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Fri, 24 Jun 2011 18:41:17 +0530 Subject: [PATCH 007/132] [EDI] Final Tesing Ymal bzr revid: aar@tinyerp.com-20110624131117-x517otc1yc1f2r97 --- addons/account/__openerp__.py | 1 + addons/account/edi_invoice.py | 60 ++++++++++++++++--- addons/account/test/test_edi_invoice.yml | 76 +++++++++++------------- 3 files changed, 86 insertions(+), 51 deletions(-) diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index 1bb1b56a476..44537ad638a 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -122,6 +122,7 @@ module named account_voucher. 'board_account_view.xml', "wizard/account_report_profit_loss_view.xml", "wizard/account_report_balance_sheet_view.xml", + ], 'demo_xml': [ 'account_demo.xml', diff --git a/addons/account/edi_invoice.py b/addons/account/edi_invoice.py index 56d0e52df95..189604e6306 100644 --- a/addons/account/edi_invoice.py +++ b/addons/account/edi_invoice.py @@ -1,6 +1,7 @@ -from osv import fields, osv,orm +from osv import fields, osv, orm from base.ir import ir_edi +from tools.translate import _ class account_invoice(osv.osv,ir_edi.edi): @@ -96,6 +97,7 @@ class account_invoice(osv.osv,ir_edi.edi): on the tax config of the DB where it is imported. """ # generic implementation! + "" partner = self.pool.get('res.partner') partner_add = self.pool.get('res.partner.address') @@ -104,6 +106,7 @@ class account_invoice(osv.osv,ir_edi.edi): product_categ = self.pool.get('product.category') acc_invoice = self.pool.get('account.invoice') company = self.pool.get('res.company') + country = self.pool.get('res.country') tax_id = [] account_id = [] partner_id = None @@ -168,16 +171,47 @@ class account_invoice(osv.osv,ir_edi.edi): u_id = model_data.search(cr, uid, [('res_id','=',browse_partner.id),('model','=',browse_partner._name)]) if len(u_id): company_id = browse_partner.company_id + xml_obj = model_data.browse(cr,uid,u_id[0]) uuid = ir_edi.safe_unique_id(browse_partner._name,browse_partner.id) db_uuid = '%s:%s' % (uuid,xml_obj.name) part.update({'partner_id':[db_uuid,browse_partner.name]}) + else: + + company_address = {} company_id = company.create(cr, uid, {'name':edi_document['company_id'][1]}) - add_id = partner_add.create(cr,uid,edi_document['company_address']) - res.update({'name': edi_document['company_id'][1],'supplier': True, 'partner_id': edi_document['partner_id'],'address':add_id}) - partner_id = partner.create(cr,uid,res) + + for key in edi_document['company_address'].keys(): + if type(edi_document['company_address'][key]).__name__ == 'list': + if edi_document['company_address'][key][1] is not None: + country_id = country.search(cr ,uid,[('name','=',edi_document['company_address'][key][1])]) + + if len(country_id): + company_address.update({key : country_id[0]}) + + else: + if isinstance(edi_document['company_address'][key][1],unicode): + country_name = str(edi_document['company_address'][key][1]) + country_code = country_name[:2].upper() + country_id = country.create(cr, uid, {'code': country_code,name: country_name}) + company_address.update({key : country_id[0]}) + else: + company_address.update({key : edi_document['company_address'][key][1]}) + else: + company_address.update({key:edi_document['company_address'][key]}) + + add_id = [] + partner_id = [] + + add_id = partner_add.create(cr,uid,company_address) + + res.update({'name': edi_document['company_id'][1],'supplier': True,'address': [unicode(add_id)], 'company_id': unicode(company_id),'country' : country_id}) + + partner_id.append(partner.create(cr,uid,{'name': edi_document['company_id'][1],'supplier': True,'address': unicode(add_id), 'company_id': unicode(company_id),'country' : country_id})) + browse_partner = partner.browse(cr,uid,partner_id[0]) + company_id = browse_partner.company_id u_id = model_data.search(cr, uid, [('res_id','=',browse_partner.id),('model','=',browse_partner._name)]) if len(u_id): xml_obj = model_data.browse(cr,uid,u_id[0]) @@ -200,15 +234,21 @@ class account_invoice(osv.osv,ir_edi.edi): del edi_document['partner_id'] del edi_document['company_id'] edi_document.update(part) - edi_document.update(comp) + edi_document.update(comp) + + if len(partner_id): p = self.pool.get('res.partner').browse(cr, uid, partner_id[0]) + + partner_id = int(partner_id[0]) + if company_id: if p.property_account_receivable.company_id.id != company_id.id and p.property_account_payable.company_id.id != company_id.id: property_obj = self.pool.get('ir.property') rec_pro_id = property_obj.search(cr,uid,[('name','=','property_account_receivable'),('res_id','=','res.partner,'+str(partner_id)+''),('company_id','=',company_id.id)]) - pay_pro_id = property_obj.search(cr,uid,[('name','=','property_account_payable'),('res_id','=','res.partner,'+str(partner_id)+''),('company_id','=',company_id)]) + pay_pro_id = property_obj.search(cr,uid,[('name','=','property_account_payable'),('res_id','=','res.partner,'+str(partner_id)+''),('company_id','=',company_id.id)]) + if not rec_pro_id: rec_pro_id = property_obj.search(cr,uid,[('name','=','property_account_receivable'),('company_id','=',company_id.id)]) if not pay_pro_id: @@ -218,8 +258,8 @@ class account_invoice(osv.osv,ir_edi.edi): rec_res_id = rec_line_data and rec_line_data[0].get('value_reference',False) and int(rec_line_data[0]['value_reference'].split(',')[1]) or False pay_res_id = pay_line_data and pay_line_data[0].get('value_reference',False) and int(pay_line_data[0]['value_reference'].split(',')[1]) or False if not rec_res_id and not pay_res_id: - raise osv.except_osv(_('Configuration Error !'), - _('Can not find account chart for this company, Please Create account.')) + raise osv.except_osv(_('Configuration Error !'), _('Can not find account chart for this company, Please Create account.')) + account_obj = self.pool.get('account.account') rec_obj_acc = account_obj.browse(cr, uid, [rec_res_id]) pay_obj_acc = account_obj.browse(cr, uid, [pay_res_id]) @@ -230,7 +270,9 @@ class account_invoice(osv.osv,ir_edi.edi): acc_obj = p.property_account_receivable else: acc_obj = p.property_account_payable + res_id = model_data.search(cr,uid,[('model','=',acc_obj._name),('res_id','=',acc_obj.id)]) + if len(res_id): xml_obj = model_data.browse(cr, uid, res_id[0]) uuid = ir_edi.safe_unique_id(acc_obj._name,acc_obj.id) @@ -240,7 +282,7 @@ class account_invoice(osv.osv,ir_edi.edi): edi_document.update({'reference':edi_document['internal_number'],'reference_type' : 'none'}) edi_document['internal_number'] = False context['type'] = edi_document['type'] - + del edi_document['company_address'] return super(account_invoice,self).edi_import(cr, uid, edi_document) diff --git a/addons/account/test/test_edi_invoice.yml b/addons/account/test/test_edi_invoice.yml index 9d624489c8c..ec0dead338c 100644 --- a/addons/account/test/test_edi_invoice.yml +++ b/addons/account/test/test_edi_invoice.yml @@ -1,7 +1,7 @@ - create Company - - !record {model: res.company, id: res_company_test}: + !record {model: res.company, id: res_company_test11}: name: Thomson pvt. ltd. partner_id: 1 rml_header: 1 @@ -10,62 +10,54 @@ currency_id: 1 - - create customer + create partner - - !record {model: res.partner, id: res_partner_test}: + !record {model: res.partner, id: res_partner_test20}: name: Junjun wala supplier: False - company_id: res_company_test - address: - - partner_id: res_partner_test - type: default - name: ivan - street: m g road - street2: line 28 - city: paris + company_id: res_company_test11 -- - Creating a customer invoice record -- - !record {model: account.invoice, id: account_invoice_test}: - amount_tax: 0.0 - amount_total: 83.0 - amount_untaxed: 83.0 +- + create customer invoice +- + !record {model: account.invoice, id: customer_invoice_test}: + journal_id: 1 + partner_id: res_partner_test20 currency_id: base.EUR + address_invoice_id: base.res_partner_address_11 + company_id: base.main_company + account_id: 1 date_invoice: '2011-06-22' - invoice_line: - name: '[CPU1] Processor AMD Athlon XP 1800+' - partner_id: res_partner_test - price_subtotal: 75.0 - price_unit: 75.0 - product_id: product.product_product_cpu1 - quantity: 1.0 - journal_id: 'False' name: Nothing - partner_id: res_partner_test - payment_term: account.account_payment_term - reference_type: none - + type: 'out_invoice' + invoice_line: + - product_id: product.product_product_pc1 + partner_id: res_partner_test20 + uos_id: 1 + quantity: 1.0 + price_unit: 10.0 + name: 'basic pc' + account_id: 1 + - Tesing of Export functionality and Import Functionality - !python {model: ir.edi.document}: | invoice_obj = self.pool.get('account.invoice') - invoice_ids = invoice_obj.search(cr, uid, [partner_id]) - if invoice_ids: - invoices = invoice_obj.browse(cr, uid, invoice_ids) - tokens = self.export_edi(cr, uid, invoices) - for token in tokens: - document = self.get_document(cr, uid, token, context=context) - a = self.import_edi(cr, uid, edi_document = document) - + #invoice_ids = invoice_obj.search(cr, uid, ['account.customer_invoice_test']) + #if invoice_ids: + invoices = invoice_obj.browse(cr, uid, [ref("customer_invoice_test")]) + tokens = self.export_edi(cr, uid, invoices) + for token in tokens: + document = self.get_document(cr, uid, token, context=context) + a = self.import_edi(cr, uid, edi_document = document) + - - Check after import of document customer become supplier or not + Check the customer invoice is exported or not - !python {model: account.invoice}: | - ids=self.search(cr, uid, [('partner_id','=',base.main_company),('company_id','=',res_company_test),('type','=','in_invoice')]) + ids=self.search(cr, uid, [('partner_id','=',ref("res_partner_test20")),('company_id','=',ref("res_company_test11")),('type','=','out_invoice')]) if not ids: raise AssertionError("Invoice is not imported") - - \ No newline at end of file + From b2f73c0010ae84da417906a28300120b3571eec6 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Mon, 27 Jun 2011 14:02:28 +0530 Subject: [PATCH 008/132] [REVIEW+IMP] account, EDI: clean export_edi method of account.invoice bzr revid: hmo@tinyerp.com-20110627083228-cmiuptih2y8kjlqd --- addons/account/__openerp__.py | 5 +- addons/account/edi_invoice.py | 78 ++++++++++++++---------- addons/account/test/test_edi_invoice.yml | 1 + 3 files changed, 49 insertions(+), 35 deletions(-) diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index 44537ad638a..5ec724ed462 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -143,10 +143,9 @@ module named account_voucher. 'test/account_fiscalyear_close.yml', 'test/account_bank_statement.yml', 'test/account_cash_statement.yml', - 'test/account_report.yml', 'test/test_edi_invoice.yml', - - ], + 'test/account_report.yml', + ], 'installable': True, 'active': False, 'certificate': '0080331923549', diff --git a/addons/account/edi_invoice.py b/addons/account/edi_invoice.py index 7020a8115ad..ba3675a15f4 100644 --- a/addons/account/edi_invoice.py +++ b/addons/account/edi_invoice.py @@ -23,13 +23,11 @@ from osv import fields, osv, orm from base.ir import ir_edi from tools.translate import _ -class account_invoice(osv.osv,ir_edi.edi): +class account_invoice(osv.osv, ir_edi.edi): _inherit = 'account.invoice' - def edi_export(self, cr, uid, ids, edi_struct=None, context=None): + def edi_export(self, cr, uid, records, edi_struct=None, context=None): """Exports a supplier or customer invoice""" - rec_id = [ids[0].id] - edi_struct = { 'name': True, 'origin': True, @@ -63,35 +61,51 @@ class account_invoice(osv.osv,ir_edi.edi): 'tax_amount': True, }, } - # Get EDI doc based on this struct. The result will also contain - # all metadata fields and attachments. - - edi_doc = super(account_invoice,self).edi_export(cr, uid, ids, edi_struct, context) - for i, invoice in enumerate(self.browse(cr, uid, rec_id, context=context)): - # add specific data for import - inv_comp = invoice.company_id - comp_partner = inv_comp.partner_id - comp_partner_addr = comp_partner.address - for address in comp_partner_addr: - edi_doc[i].update({ - # Add company info and address - 'company_address': { - 'street': address.street, - 'street2': address.street2, - 'zip': address.zip, - 'city': address.city, - 'state_id': self.edi_m2o(cr, uid, address.state_id), - 'country_id': self.edi_m2o(cr, uid, address.country_id), - 'email': address.email, - 'phone': address.phone - }, - # Function fields are not included in normal export + partner_pool = self.pool.get('res.partner') + partner_address_pool = self.pool.get('res.partner.address') + company_address_dict = { + 'street': True, + 'street2': True, + 'zip': True, + 'city': True, + 'state_id': True, + 'country_id': True, + 'email': True, + 'phone': True, + + } + edi_doc_list = [] + for invoice in records: + # Get EDI doc based on struct. The result will also contain all metadata fields and attachments. + edi_doc = super(account_invoice,self).edi_export(cr, uid, [invoice], edi_struct, context) + if not edi_doc: + continue + edi_doc = edi_doc[0] - #'company_logo': inv_comp.logo,#TODO - #'paid': inv_comp.paid, #TODO - }) - - return edi_doc + # Add company info and address + res = partner_pool.address_get(cr, uid, [invoice.company_id.partner_id.id], ['contact', 'invoice']) + contact_addr_id = res['contact'] + invoice_addr_id = res['invoice'] + + address = partner_address_pool.browse(cr, uid, invoice_addr_id, context=context) + edi_company_address_dict = {} + for key, value in company_address_dict.items(): + if not value: + continue + address_rec = getattr(address, key) + if not address_rec: + continue + if key.endswith('_id'): + address_rec = self.edi_m2o(cr, uid, address_rec, context=context) + edi_company_address_dict[key] = address_rec + + edi_doc.update({ + 'company_address': edi_company_address_dict, + #'company_logo': inv_comp.logo,#TODO + #'paid': inv_comp.paid, #TODO + }) + edi_doc_list.append(edi_doc) + return edi_doc_list def edi_import(self, cr, uid, edi_document, context=None): diff --git a/addons/account/test/test_edi_invoice.yml b/addons/account/test/test_edi_invoice.yml index 75c39d8aaf0..f66932f0464 100644 --- a/addons/account/test/test_edi_invoice.yml +++ b/addons/account/test/test_edi_invoice.yml @@ -48,6 +48,7 @@ tokens = self.export_edi(cr, uid, invoices) for token in tokens: document = self.get_document(cr, uid, token, context=context) + print document a = self.import_edi(cr, uid, edi_document = document) - From 3154cb96520b6a279e95f5cde74b5005cee472ab Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Mon, 27 Jun 2011 17:56:16 +0530 Subject: [PATCH 009/132] [REF] account, edi: refactor code of edi_import method bzr revid: hmo@tinyerp.com-20110627122616-15e6y17d29bbricb --- addons/account/edi_invoice.py | 295 +++++++++-------------- addons/account/test/test_edi_invoice.yml | 5 +- 2 files changed, 119 insertions(+), 181 deletions(-) diff --git a/addons/account/edi_invoice.py b/addons/account/edi_invoice.py index ba3675a15f4..5d6d92df584 100644 --- a/addons/account/edi_invoice.py +++ b/addons/account/edi_invoice.py @@ -131,192 +131,131 @@ class account_invoice(osv.osv, ir_edi.edi): on the tax config of the DB where it is imported. """ - partner = self.pool.get('res.partner') - partner_add = self.pool.get('res.partner.address') - model_data = self.pool.get('ir.model.data') - product_obj = self.pool.get('product.product') - product_categ = self.pool.get('product.category') - acc_invoice = self.pool.get('account.invoice') - company = self.pool.get('res.company') - country = self.pool.get('res.country') + partner_pool = self.pool.get('res.partner') + partner_address_pool = self.pool.get('res.partner.address') + model_data_pool = self.pool.get('ir.model.data') + product_pool = self.pool.get('product.product') + product_categ_pool = self.pool.get('product.category') + company_pool = self.pool.get('res.company') + country_pool = self.pool.get('res.country') + state_pool = self.pool.get('res.country.state') + account_journal_pool = self.pool.get('account.journal') + invoice_line_pool = self.pool.get('account.invoice.line') + account_pool = self.pool.get('account.account') tax_id = [] account_id = [] partner_id = None company_id = None if context is None: context = {} - for field in edi_document.keys(): - if field == 'type': - if len(edi_document['invoice_line']): - name = edi_document['invoice_line'][0]['product_id'][1] - else: - name = None - re_ids = product_obj.search(cr,uid,[('name','=',name)]) - if edi_document['type'] == 'out_invoice' or edi_document['type'] == 'out_refund': - if re_ids: - if product_obj.browse(cr,uid,re_ids)[0].property_account_expense: - account_id = product_obj.browse(cr,uid,re_ids)[0].property_account_expense - else: - account_id = product_categ.browse(cr,uid,re_ids)[0].property_account_expense_categ - if product_obj.browse(cr,uid,re_ids)[0].taxes_id: - tax_id = product_obj.browse(cr, uid,re_ids)[0].taxes_id - if edi_document['type'] == 'out_refund': - edi_document['type'] = 'in_refund' - else: - edi_document['type'] = 'in_invoice' - elif edi_document['type'] == 'in_invoice' or edi_document['type'] == 'in_refund': - if re_ids: - if product_obj.browse(cr,uid,re_ids)[0].property_account_income: - account_id = product_obj.browse(cr,uid,re_ids)[0].property_account_income - else: - account_id = product_categ.browse(cr,uid,re_ids)[0].property_account_income_categ - if product_obj.browse(cr,uid,re_ids)[0].taxes_id: - tax_id = product_obj.browse(cr, uid,re_ids)[0].taxes_id - if edi_document['type'] == 'in_refund': - edi_document['type'] = 'out_refund' - else: - edi_document['type'] = 'out_invoice' - - if account_id: - name_ids = model_data.search(cr, uid, [('model','=',account_id._name),('res_id','=',account_id.id)]) - if name_ids: - xml_id = model_data.browse(cr, uid, name_ids)[0].name - db_uuid = ir_edi.safe_unique_id(account_id._name,account_id.id) - edi_document['invoice_line'][0]['account_id'] = [db_uuid+':'+xml_id,account_id.name] - - if tax_id: - name_ids = model_data.search(cr, uid, [('model','=',tax_id[0]._name),('res_id','=',tax_id[0].id)]) - if name_ids: - xml_id = model_data.browse(cr, uid, name_ids)[0].name - db_uuid = ir_edi.safe_unique_id(tax_id[0]._name,tax_id[0].id) - edi_document['tax_line'][0]['account_id'] = [db_uuid+':'+xml_id,tax_id[0].name] - else: - if len(edi_document['tax_line']): - edi_document['tax_line'][0]['manual'] = True - - res = {} - part = {} - comp = {} - partner_id = partner.search(cr,uid,[('name','=',edi_document['company_id'][1])]) - if len(partner_id): - browse_partner = partner.browse(cr,uid,partner_id[0]) - u_id = model_data.search(cr, uid, [('res_id','=',browse_partner.id),('model','=',browse_partner._name)]) - if len(u_id): - company_id = browse_partner.company_id - - xml_obj = model_data.browse(cr,uid,u_id[0]) - uuid = ir_edi.safe_unique_id(browse_partner._name,browse_partner.id) - db_uuid = '%s:%s' % (uuid,xml_obj.name) - part.update({'partner_id':[db_uuid,browse_partner.name]}) - - else: - - company_address = {} - company_id = company.create(cr, uid, {'name':edi_document['company_id'][1]}) - - for key in edi_document['company_address'].keys(): - if type(edi_document['company_address'][key]).__name__ == 'list': - if edi_document['company_address'][key][1] is not None: - country_id = country.search(cr ,uid,[('name','=',edi_document['company_address'][key][1])]) - - if len(country_id): - company_address.update({key : country_id[0]}) - - else: - if isinstance(edi_document['company_address'][key][1],unicode): - country_name = str(edi_document['company_address'][key][1]) - country_code = country_name[:2].upper() - country_id = country.create(cr, uid, {'code': country_code,name: country_name}) - company_address.update({key : country_id[0]}) - else: - company_address.update({key : edi_document['company_address'][key][1]}) - else: - company_address.update({key:edi_document['company_address'][key]}) - - add_id = [] - partner_id = [] - - add_id = partner_add.create(cr,uid,company_address) - - res.update({'name': edi_document['company_id'][1],'supplier': True,'address': [unicode(add_id)], 'company_id': unicode(company_id),'country' : country_id}) - - partner_id.append(partner.create(cr,uid,{'name': edi_document['company_id'][1],'supplier': True,'address': unicode(add_id), 'company_id': unicode(company_id),'country' : country_id})) - - browse_partner = partner.browse(cr,uid,partner_id[0]) - company_id = browse_partner.company_id - u_id = model_data.search(cr, uid, [('res_id','=',browse_partner.id),('model','=',browse_partner._name)]) - if len(u_id): - xml_obj = model_data.browse(cr,uid,u_id[0]) - uuid = ir_edi.safe_unique_id(browse_partner._name,browse_partner.id) - db_uuid = '%s:%s' % (uuid,xml_obj.name) - part.update({'partner_id':[db_uuid,browse_partner.name]}) - - comp_id = partner.search(cr,uid,[('name','=',edi_document['partner_id'][1])]) - - if len(comp_id): - browse_partner = partner.browse(cr,uid,comp_id[0]) - browse_company = company.browse(cr,uid,browse_partner.company_id.id) - u_id = u_id = model_data.search(cr, uid, [('res_id','=',browse_company.id),('model','=',browse_company._name)]) - if len(u_id): - xml_obj = model_data.browse(cr,uid,u_id[0]) - uuid = ir_edi.safe_unique_id(browse_company._name,browse_company.id) - db_uuid = '%s:%s' % (uuid,xml_obj.name) - comp.update({'company_id':[db_uuid,browse_company.name]}) - - del edi_document['partner_id'] - del edi_document['company_id'] - edi_document.update(part) - edi_document.update(comp) - - - if len(partner_id): - p = self.pool.get('res.partner').browse(cr, uid, partner_id[0]) - - partner_id = int(partner_id[0]) - - if company_id: - if p.property_account_receivable.company_id.id != company_id.id and p.property_account_payable.company_id.id != company_id.id: - property_obj = self.pool.get('ir.property') - - rec_pro_id = property_obj.search(cr,uid,[('name','=','property_account_receivable'),('res_id','=','res.partner,'+str(partner_id)+''),('company_id','=',company_id.id)]) - pay_pro_id = property_obj.search(cr,uid,[('name','=','property_account_payable'),('res_id','=','res.partner,'+str(partner_id)+''),('company_id','=',company_id.id)]) - - if not rec_pro_id: - rec_pro_id = property_obj.search(cr,uid,[('name','=','property_account_receivable'),('company_id','=',company_id.id)]) - if not pay_pro_id: - pay_pro_id = property_obj.search(cr,uid,[('name','=','property_account_payable'),('company_id','=',company_id.id)]) - rec_line_data = property_obj.read(cr,uid,rec_pro_id,['name','value_reference','res_id']) - pay_line_data = property_obj.read(cr,uid,pay_pro_id,['name','value_reference','res_id']) - rec_res_id = rec_line_data and rec_line_data[0].get('value_reference',False) and int(rec_line_data[0]['value_reference'].split(',')[1]) or False - pay_res_id = pay_line_data and pay_line_data[0].get('value_reference',False) and int(pay_line_data[0]['value_reference'].split(',')[1]) or False - if not rec_res_id and not pay_res_id: - raise osv.except_osv(_('Configuration Error !'), _('Can not find account chart for this company, Please Create account.')) - - account_obj = self.pool.get('account.account') - rec_obj_acc = account_obj.browse(cr, uid, [rec_res_id]) - pay_obj_acc = account_obj.browse(cr, uid, [pay_res_id]) - p.property_account_receivable = rec_obj_acc[0] - p.property_account_payable = pay_obj_acc[0] - if edi_document['type'] in ('out_invoice', 'out_refund'): - acc_obj = p.property_account_receivable - else: - acc_obj = p.property_account_payable - - res_id = model_data.search(cr,uid,[('model','=',acc_obj._name),('res_id','=',acc_obj.id)]) - - if len(res_id): - xml_obj = model_data.browse(cr, uid, res_id[0]) - uuid = ir_edi.safe_unique_id(acc_obj._name,acc_obj.id) - db_uuid = '%s:%s' % (uuid,xml_obj.name) - edi_document.update({'account_id':[db_uuid,acc_obj.name]}) - - edi_document.update({'reference':edi_document['internal_number'],'reference_type' : 'none'}) - edi_document['internal_number'] = False - context['type'] = edi_document['type'] - + + # 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 + edi_company_address = edi_document['company_address'] + edi_partner_id = edi_document['partner_id'] + company_name = edi_document['company_id'][1] + invoice_type = edi_document['type'] + state_id = edi_company_address.get('state_id', False) + state_name = state_id and state_id[1] + country_id = edi_company_address.get('country_id', False) + country_name = country_id and country_id[1] + + country_id = country_name and self.edi_import_relation(cr, uid, 'res.country', country_name, context=context) or False + state_id = state_name and self.edi_import_relation(cr, uid, 'res.country.state', state_name, + values={'country_id': country_id, 'code': state_name}, context=context) or False + address_value = { + 'street': edi_company_address.get('street', False), + 'street2': edi_company_address.get('street2', False), + 'zip': edi_company_address.get('zip', False), + 'city': edi_company_address.get('city', False), + 'state_id': state_id, + 'country_id': country_id, + 'email': edi_company_address.get('email', False), + 'phone': edi_company_address.get('phone', False), + + } + + + partner_value = {'name': company_name} + if invoice_type in ('out_invoice', 'in_refund'): + partner_value.update({'customer': True, 'supplier': False}) + if invoice_type in ('in_invoice', 'out_refund'): + partner_value.update({'customer': False, 'supplier': True}) + + partner_id = partner_pool.create(cr, uid, partner_value, context=context) + address_value.update({'partner_id': partner_id}) + address_id = partner_address_pool.create(cr, uid, address_value, context=context) + + partner = partner_pool.browse(cr, uid, partner_id, context=context) + edi_document['partner_id'] = self.edi_m2o(cr, uid, partner, context=context) + + # change type: out_invoice'<->'in_invoice','out_refund'<->'in_refund' + invoice_type = invoice_type.startswith('in_') and invoice_type.replace('in_','out_') or invoice_type.replace('out_','in_') + edi_document['type'] = invoice_type + + # Set Account + if invoice_type in ('out_invoice', 'out_refund'): + invoice_account = partner.property_account_receivable + else: + invoice_account = partner.property_account_payable + 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 + + # company should set by default so delete company data from edi Document del edi_document['company_address'] - return super(account_invoice,self).edi_import(cr, uid, edi_document) + del edi_document['company_id'] + + # journal_id: should be selected based on type: simply put the 'type' in the context when calling create(), will be selected correctly + journal_context = context.copy() + journal_context.update({'type':invoice_type}) + journal_id = self._get_journal(cr, uid, context=journal_context) + journal = False + if journal_id: + journal = account_journal_pool.browse(cr, uid, journal_id, context=context) + edi_document['journal_id'] = journal and self.edi_m2o(cr, uid, journal, context=context) or False + + # 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.get('invoice_line', []): + product_id = edi_invoice_line.get('product_id', False) + account = False + if product_id: + product_name = product_id and product_id[1] + product_id = self.edi_import_relation(cr, uid, 'product.product', product_name, context=context) + product = product_pool.browse(cr, uid, product_id, context=context) + + if invoice_type in ('out_invoice','out_refund'): + account = product.product_tmpl_id.property_account_income + if not account: + account = product.categ_id.property_account_income_categ + else: + account = product.product_tmpl_id.property_account_expense + if not account: + account = product.categ_id.property_account_expense_categ + + # TODO: add effect of fiscal position + # account = fpos_obj.map_account(cr, uid, fiscal_position_id, account.id) + edi_invoice_line['account_id'] = account and self.edi_m2o(cr, uid, account, context=context) or False + + # 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. + for edi_tax_line in edi_document.get('tax_line', []): + account_ids = account_pool.search(cr, uid, [('type','<>','view'),('type','<>','income'), ('type', '<>', 'closed')]) + if account_ids: + edi_tax_line['account_id'] = account_ids[0] #TODO should select account of output VAT for Customer Invoice and Input VAT for Supplier Invoice + edi_tax_line['manual'] = True + + # TODO :=> payment_term: if set, create a default one based on name... + print edi_document + return super(account_invoice,self).edi_import(cr, uid, edi_document, context=context) account_invoice() diff --git a/addons/account/test/test_edi_invoice.yml b/addons/account/test/test_edi_invoice.yml index f66932f0464..18e5a8fb9e9 100644 --- a/addons/account/test/test_edi_invoice.yml +++ b/addons/account/test/test_edi_invoice.yml @@ -28,7 +28,7 @@ company_id: base.main_company account_id: 1 date_invoice: '2011-06-22' - name: Nothing + name: selling product type: 'out_invoice' invoice_line: - product_id: product.product_product_pc1 @@ -48,14 +48,13 @@ tokens = self.export_edi(cr, uid, invoices) for token in tokens: document = self.get_document(cr, uid, token, context=context) - print document a = self.import_edi(cr, uid, edi_document = document) - Check the customer invoice is exported or not - !python {model: account.invoice}: | - ids=self.search(cr, uid, [('partner_id','=',ref("res_partner_test20")),('company_id','=',ref("res_company_test11")),('type','=','out_invoice')]) + ids=self.search(cr, uid, [('partner_id','=',ref("res_partner_test20")),('type','=','out_invoice')]) if not ids: raise AssertionError("Invoice is not imported") From 99624f6b0efe76186d1c51e26d733938c087660d Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Mon, 27 Jun 2011 17:57:32 +0530 Subject: [PATCH 010/132] [REF] account, edi: remove print statement bzr revid: hmo@tinyerp.com-20110627122732-s2yswh7mtyaumjz9 --- addons/account/edi_invoice.py | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/account/edi_invoice.py b/addons/account/edi_invoice.py index 5d6d92df584..e37bd220d35 100644 --- a/addons/account/edi_invoice.py +++ b/addons/account/edi_invoice.py @@ -254,7 +254,6 @@ class account_invoice(osv.osv, ir_edi.edi): edi_tax_line['manual'] = True # TODO :=> payment_term: if set, create a default one based on name... - print edi_document return super(account_invoice,self).edi_import(cr, uid, edi_document, context=context) account_invoice() From 0441c97c5c8eef5809cf9a6dfc8982ca9e6f8d8b Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Wed, 29 Jun 2011 11:14:52 +0530 Subject: [PATCH 011/132] [REVIEW] account: correct YML of EDI bzr revid: hmo@tinyerp.com-20110629054452-2e0vsmleaouos6zv --- addons/account/test/test_edi_invoice.yml | 34 ++++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/addons/account/test/test_edi_invoice.yml b/addons/account/test/test_edi_invoice.yml index dacd80e57b1..6651448c712 100644 --- a/addons/account/test/test_edi_invoice.yml +++ b/addons/account/test/test_edi_invoice.yml @@ -1,5 +1,5 @@ - - create Company + I create a company for Customer - !record {model: res.company, id: res_company_test11}: name: Thomson pvt. ltd. @@ -10,14 +10,14 @@ currency_id: 1 - - create partner + I create a partner which is a my customer - !record {model: res.partner, id: res_partner_test20}: name: Junjun wala supplier: False company_id: res_company_test11 - - create customer invoice + I create one customer invoice - !record {model: account.invoice, id: customer_invoice_test}: journal_id: 1 @@ -45,7 +45,7 @@ amount: 1000.00 - - I open the Invoice for the SO. + I Open the Invoice - !python {model: account.invoice}: | @@ -55,30 +55,30 @@ wf_service.trg_validate(uid, 'account.invoice',invoices.id,'invoice_open', cr) - - Tesing of EDI functionality + I Tesing of EDI functionality. First I export customer invoice from my company than import that invoice into customer company - !python {model: ir.edi.document}: | - invoice_invoice_new = self.pool.get('account.invoice') - invoices = invoice_invoice_new.browse(cr, uid, [ref("customer_invoice_test")]) - tokens = self.export_edi(cr, uid, invoices) - for token in tokens: - document = self.get_document(cr, uid, token, context=context) - a = self.import_edi(cr, uid, edi_document = document) - + invoice_pool = self.pool.get('account.invoice') + invoice = invoice_pool.browse(cr, uid, ref("customer_invoice_test")) + tokens = self.export_edi(cr, uid, [invoice]) + assert tokens, 'Token is not generated' + document = self.get_document(cr, uid, tokens[0]) + a = self.import_edi(cr, uid, edi_document = document) + assert a, 'Invoice is not imported' - - Check the exported out_invoice become in_invoice or not + I Checking the out invoice become in invoice or not after import - !python {model: account.invoice}: | - invoice_old = self.browse(cr,uid,ref("customer_invoice_test")) + invoice_old = self.browse(cr, uid, ref("customer_invoice_test")) new_partner_id = self.pool.get('res.partner').name_search(cr, uid, invoice_old.company_id.name) - assert new_partner_id, 'Partner is not created of party' + assert new_partner_id, 'Partner is not created of Supplier' - ids = self.search(cr, uid, [('partner_id','=',new_partner_id[0]),('reference','=',invoice_old.internal_number)]) + ids = self.search(cr, uid, [('partner_id','=',new_partner_id[0][0]),('reference','=',invoice_old.internal_number)]) assert ids, 'Invoice does not have created of party' invoice_new = self.browse(cr, uid, ids[0]) assert invoice_new.reference == invoice_old.internal_number, "internal number is not stored in reference" - assert invoice_new.reference_type == None, "reference type is not set to 'None'" + 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" for inv_line in invoice_new.invoice_line: From d1ce89e2a5f2fb26fdc93fc461859329a9329ff0 Mon Sep 17 00:00:00 2001 From: "Hardik Ansodariy (OpenERP)" Date: Fri, 8 Jul 2011 15:00:22 +0530 Subject: [PATCH 012/132] [IMP]:Adding stock picking in edi bzr revid: han@tinyerp.com-20110708093022-qm22kull5wq1vvmi --- addons/account/__openerp__.py | 4 +- addons/account/edi_invoice_action.xml | 11 +- addons/stock/__init__.py | 3 +- addons/stock/__openerp__.py | 5 +- addons/stock/edi_stock_picking.py | 183 ++++++++++++++++++++++++++ 5 files changed, 195 insertions(+), 11 deletions(-) create mode 100644 addons/stock/edi_stock_picking.py diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index adc0e3ea7e2..d5818f0bf6d 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -122,14 +122,14 @@ module named account_voucher. 'board_account_view.xml', "wizard/account_report_profit_loss_view.xml", "wizard/account_report_balance_sheet_view.xml", - + #"edi_invoice_action.xml", ], 'demo_xml': [ 'account_demo.xml', 'project/project_demo.xml', 'project/analytic_account_demo.xml', 'demo/account_minimal.xml', - "edi_invoice_action.xml", + #"edi_invoice_action.xml", # 'account_unit_test.xml', ], 'test': [ diff --git a/addons/account/edi_invoice_action.xml b/addons/account/edi_invoice_action.xml index e5d7a3f5022..a7bb3d5edf7 100644 --- a/addons/account/edi_invoice_action.xml +++ b/addons/account/edi_invoice_action.xml @@ -3,7 +3,7 @@ - context.update({'token':self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)}) + context.update({'token':self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context),'invoice':object.number,'company':object.company_id.name,'currency':object.currency_id.name,'amount':object.amount_total,'date_due':object.date_due}) code ir.actions.server @@ -49,15 +49,14 @@ xyz - test ${object._context.get('token')} -${object.account_id.currency_mode} + Hello, -We just registered the invoice 'INV/001' for 121 EUR for your company 'ASUSTek'. +We just registered the invoice ${object.number} for ${object.amount}, ${object.currency} for your company ${object.company}. You can click on the following link to preview, print and pay this invoice: - http://URL_ON_WEB_CLIENT + http://localhost:8080/openerp/token? ${object._context.get('token')} -Please note that this invoice has to be paid before 12/04/2011. +Please note that this invoice has to be paid before ${object.date_due} Regards, mako diff --git a/addons/stock/__init__.py b/addons/stock/__init__.py index 78abee6d31d..4c4634babc1 100644 --- a/addons/stock/__init__.py +++ b/addons/stock/__init__.py @@ -24,5 +24,6 @@ import partner import product import report import wizard +import edi_stock_picking -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock/__openerp__.py b/addons/stock/__openerp__.py index 1a33fd3012c..9e743b6ff0d 100644 --- a/addons/stock/__openerp__.py +++ b/addons/stock/__openerp__.py @@ -48,7 +48,7 @@ Thanks to the double entry management, the inventory controlling is powerful and "depends" : ["product", "account"], "category" : "Warehouse", "init_xml" : [], - "demo_xml" : ["stock_demo.xml"], + #"demo_xml" : ["stock_demo.xml"], "update_xml" : [ "security/stock_security.xml", "security/ir.model.access.csv", @@ -75,7 +75,8 @@ Thanks to the double entry management, the inventory controlling is powerful and "partner_view.xml", "report/report_stock_move_view.xml", "report/report_stock_view.xml", - "board_warehouse_view.xml" + "board_warehouse_view.xml", + "test/edi_stock_picking.yml", ], 'test': ['test/stock_test.yml', 'test/stock_report.yml', diff --git a/addons/stock/edi_stock_picking.py b/addons/stock/edi_stock_picking.py new file mode 100644 index 00000000000..d74a3ef5665 --- /dev/null +++ b/addons/stock/edi_stock_picking.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). +# +# 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 . +# +############################################################################## + +from osv import fields, osv, orm +from base.ir import ir_edi +from tools.translate import _ + +class stock_picking(osv.osv, ir_edi.edi): + _inherit = 'stock.picking' + + def edi_export(self, cr, uid, records, edi_struct=None, context=None): + """Exports a supplier or customer invoice""" + edi_struct = { + + 'origin': True, + 'type': True, + 'stock_journal_id': True, + 'move_type': True, + 'date': True, + 'date_done': True, + 'move_lines': { + 'name': True, + 'date': True, + 'date_expected': True, + 'product_id': True, + 'product_qty': True, + 'product_uom': True, + 'location_id': True, + 'location_dest_id': True, + 'company_id': True, + }, + 'invoice_state': True, + 'address_id': True, + 'company_id': True, + } + partner_pool = self.pool.get('res.partner') + partner_address_pool = self.pool.get('res.partner.address') + company_address_dict = { + 'street': True, + 'street2': True, + 'zip': True, + 'city': True, + 'state_id': True, + 'country_id': True, + 'email': True, + 'phone': True, + + } + edi_doc_list = [] + for picking in records: + # Get EDI doc based on struct. The result will also contain all metadata fields and attachments. + edi_doc = super(stock_picking,self).edi_export(cr, uid, [picking], edi_struct, context) + if not edi_doc: + continue + edi_doc = edi_doc[0] + + # Add company info and address + res = partner_pool.address_get(cr, uid, [picking.company_id.partner_id.id], ['contact', 'picking']) + contact_addr_id = res['contact'] + invoice_addr_id = res['picking'] + + address = partner_address_pool.browse(cr, uid, invoice_addr_id, context=context) + edi_company_address_dict = {} + for key, value in company_address_dict.items(): + if not value: + continue + address_rec = getattr(address, key) + if not address_rec: + continue + if key.endswith('_id'): + address_rec = self.edi_m2o(cr, uid, address_rec, context=context) + edi_company_address_dict[key] = address_rec + + edi_doc.update({ + 'company_address': edi_company_address_dict, + #'company_logo': inv_comp.logo,#TODO + #'paid': inv_comp.paid, #TODO + }) + edi_doc_list.append(edi_doc) + return edi_doc_list + + def edi_import(self, cr, uid, edi_document, context=None): + + partner_pool = self.pool.get('res.partner') + partner_address_pool = self.pool.get('res.partner.address') + model_data_pool = self.pool.get('ir.model.data') + product_pool = self.pool.get('product.product') + product_categ_pool = self.pool.get('product.category') + company_pool = self.pool.get('res.company') + country_pool = self.pool.get('res.country') + state_pool = self.pool.get('res.country.state') + account_journal_pool = self.pool.get('account.journal') + invoice_line_pool = self.pool.get('account.invoice.line') + account_pool = self.pool.get('account.account') + tax_id = [] + account_id = [] + partner_id = None + company_id = None + if context is None: + context = {} + + # 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 + edi_company_address = edi_document['company_address'] + + company_name = edi_document['company_id'][1] + shipping_type = edi_document['type'] + state_id = edi_company_address.get('state_id', False) + state_name = state_id and state_id[1] + country_id = edi_company_address.get('country_id', False) + country_name = country_id and country_id[1] + + country_id = country_name and self.edi_import_relation(cr, uid, 'res.country', country_name, context=context) or False + state_id = state_name and self.edi_import_relation(cr, uid, 'res.country.state', state_name, + values={'country_id': country_id, 'code': state_name}, context=context) or False + address_value = { + 'street': edi_company_address.get('street', False), + 'street2': edi_company_address.get('street2', False), + 'zip': edi_company_address.get('zip', False), + 'city': edi_company_address.get('city', False), + 'state_id': state_id, + 'country_id': country_id, + 'email': edi_company_address.get('email', False), + 'phone': edi_company_address.get('phone', False), + + } + + + partner_value = {'name': company_name} + partner_id = partner_pool.search(cr, uid, [('name','=',company_name)]) + if len(partner_id): + partner_id = partner_pool.browse(cr, uid, partner_id[0], context=context) + address_id = partner_id.address[0].id + else: + partner_id = partner_pool.create(cr, uid, partner_value, context=context) + address_value.update({'partner_id': partner_id}) + address_id = partner_address_pool.create(cr, uid, address_value, context=context) + partner_address = partner_address_pool.browse(cr, uid, address_id, context=context) + edi_document.update({'address_id': self.edi_m2o(cr, uid, partner_address, context=context)}) + + + + + # change type: out'<->'in for shipping + shipping_type = shipping_type.startswith('in') and shipping_type.replace('in','out') or shipping_type.replace('out','in') + edi_document['type'] = shipping_type + company_id = edi_document['company_id'] + #print ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",edi_document['move_lines'] + edi_document.update({'company_id': edi_document['move_lines'][0]['company_id']}) + edi_document['move_lines'][0].update({'company_id':company_id}) + + del edi_document['company_address'] + + + + return super(stock_picking,self).edi_import(cr, uid, edi_document, context=context) + +stock_picking() + +class stock_move(osv.osv, ir_edi.edi): + _inherit='stock.move' + +stock_move() + From 30b5f52c572e39f751227fc4301387b539c2a9b4 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Wed, 13 Jul 2011 15:49:07 +0530 Subject: [PATCH 013/132] [IMP] account: add some fields in EDI Dict of account.invoice which are display in Web EDI Interface bzr revid: hmo@tinyerp.com-20110713101907-oo7le07rtkwzpsdu --- addons/account/edi_invoice.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/addons/account/edi_invoice.py b/addons/account/edi_invoice.py index f8f615b40fc..7c69b52b879 100644 --- a/addons/account/edi_invoice.py +++ b/addons/account/edi_invoice.py @@ -35,6 +35,10 @@ class account_invoice(osv.osv, ir_edi.edi): 'type': True, # -> reversed at import 'internal_number': True, # -> reference at import 'comment': True, + 'reference': True, + 'amount_untaxed': True, + 'amount_tax': True, + 'amount_total': True, 'date_invoice': True, 'date_due': True, 'partner_id': True, @@ -47,6 +51,7 @@ class account_invoice(osv.osv, ir_edi.edi): 'uos_id': True, 'product_id': True, 'price_unit': True, + 'price_subtotal': True, 'quantity': True, 'discount': True, 'note': True, From e087243c241382e59cae48a87289fa00f501415c Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Thu, 14 Jul 2011 16:27:16 +0530 Subject: [PATCH 014/132] [REVIEW] account: rename file name of edi_invoice_action_data bzr revid: hmo@tinyerp.com-20110714105716-xn54phkryzjuc94e --- addons/account/__openerp__.py | 3 +-- .../{edi_invoice_action.xml => edi_invoice_action_data.xml} | 0 addons/stock/__init__.py | 1 - addons/stock/__openerp__.py | 3 +-- 4 files changed, 2 insertions(+), 5 deletions(-) rename addons/account/{edi_invoice_action.xml => edi_invoice_action_data.xml} (100%) diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index d5818f0bf6d..a0796fcf6b8 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -122,14 +122,13 @@ module named account_voucher. 'board_account_view.xml', "wizard/account_report_profit_loss_view.xml", "wizard/account_report_balance_sheet_view.xml", - #"edi_invoice_action.xml", + "edi_invoice_action_data.xml", ], 'demo_xml': [ 'account_demo.xml', 'project/project_demo.xml', 'project/analytic_account_demo.xml', 'demo/account_minimal.xml', - #"edi_invoice_action.xml", # 'account_unit_test.xml', ], 'test': [ diff --git a/addons/account/edi_invoice_action.xml b/addons/account/edi_invoice_action_data.xml similarity index 100% rename from addons/account/edi_invoice_action.xml rename to addons/account/edi_invoice_action_data.xml diff --git a/addons/stock/__init__.py b/addons/stock/__init__.py index 4c4634babc1..0a241209593 100644 --- a/addons/stock/__init__.py +++ b/addons/stock/__init__.py @@ -24,6 +24,5 @@ import partner import product import report import wizard -import edi_stock_picking # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock/__openerp__.py b/addons/stock/__openerp__.py index 9e743b6ff0d..f4facc9c4f7 100644 --- a/addons/stock/__openerp__.py +++ b/addons/stock/__openerp__.py @@ -48,7 +48,7 @@ Thanks to the double entry management, the inventory controlling is powerful and "depends" : ["product", "account"], "category" : "Warehouse", "init_xml" : [], - #"demo_xml" : ["stock_demo.xml"], + "demo_xml" : ["stock_demo.xml"], "update_xml" : [ "security/stock_security.xml", "security/ir.model.access.csv", @@ -76,7 +76,6 @@ Thanks to the double entry management, the inventory controlling is powerful and "report/report_stock_move_view.xml", "report/report_stock_view.xml", "board_warehouse_view.xml", - "test/edi_stock_picking.yml", ], 'test': ['test/stock_test.yml', 'test/stock_report.yml', From ced29b87a0a05c41ec8f505139b97babe636e711 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Thu, 14 Jul 2011 16:32:55 +0530 Subject: [PATCH 015/132] [IMP] account: add dependancy of email_template module bzr revid: hmo@tinyerp.com-20110714110255-65ny7ujd5arcxz37 --- addons/account/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index a0796fcf6b8..6e6d1b79f99 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -52,7 +52,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" : ["product", "analytic", "process","board"], + "depends" : ["product", "analytic", "process","board", "email_template"], 'update_xml': [ 'security/account_security.xml', 'security/ir.model.access.csv', From 7e77228d291cddc80b52483d84931b2423a8c739 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Fri, 15 Jul 2011 13:14:42 +0530 Subject: [PATCH 016/132] [REVIEW] account: correct server action and email template data and add default email template bzr revid: hmo@tinyerp.com-20110715074442-w9u3y0wv3s5omoll --- addons/account/edi_invoice_action_data.xml | 63 +++++++------------ .../email_template_scheduler_data.xml | 8 +++ 2 files changed, 30 insertions(+), 41 deletions(-) diff --git a/addons/account/edi_invoice_action_data.xml b/addons/account/edi_invoice_action_data.xml index a7bb3d5edf7..75e814216e7 100644 --- a/addons/account/edi_invoice_action_data.xml +++ b/addons/account/edi_invoice_action_data.xml @@ -2,69 +2,50 @@ - - context.update({'token':self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context),'invoice':object.number,'company':object.company_id.name,'currency':object.currency_id.name,'amount':object.amount_total,'date_due':object.date_due}) + + context.update({'edi_web_url_view': '%s/edi/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) +if object.partner_id.opt_out: self.pool.get('email.template').generate_mail(cr, + uid, + self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'email_template_edi_invoice')[1], + [object.id], + context=context) code ir.actions.server True - invoice - - - - - - hello - invoice created - email - ir.actions.server - han@tinyerp.com - - True - invoice_send - - - - - - other - ir.actions.server - - - True - Multi_action + EDI Document - Invoice + - + + - - - invoice - xyz - + + + ${object.company_id.name} - Invoice ${object.number} + ${object.address_invoice_id.email} - xyz + True -Hello, +Hello ${object.partner_id.name}, -We just registered the invoice ${object.number} for ${object.amount}, ${object.currency} for your company ${object.company}. +We just registered the invoice ${object.number} for ${object.amount_total} ${object.currency_id.name} for your company ${object.company_id.name}. You can click on the following link to preview, print and pay this invoice: - http://localhost:8080/openerp/token? ${object._context.get('token')} + + ${object._context.get('edi_web_url_view')} Please note that this invoice has to be paid before ${object.date_due} Regards, mako - english - xyz - Invoice + Mail Template of Invoice For EDI Document account.invoice - xyz + ${object.user_id.email or ''} diff --git a/addons/email_template/email_template_scheduler_data.xml b/addons/email_template/email_template_scheduler_data.xml index 45a9be04ac7..910aa8572e6 100644 --- a/addons/email_template/email_template_scheduler_data.xml +++ b/addons/email_template/email_template_scheduler_data.xml @@ -1,6 +1,14 @@ + + + Default EMail Template Account + test@localhost + draft + + + Email Template scheduler From 17e25609ed72ebce2ce7edf33ae54d39a4f5efec Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Fri, 15 Jul 2011 14:02:49 +0530 Subject: [PATCH 017/132] [REM] stock: EDI feature will be included later bzr revid: hmo@tinyerp.com-20110715083249-02cutonhjovv2z2k --- addons/stock/edi_stock_picking.py | 183 ------------------------------ 1 file changed, 183 deletions(-) delete mode 100644 addons/stock/edi_stock_picking.py diff --git a/addons/stock/edi_stock_picking.py b/addons/stock/edi_stock_picking.py deleted file mode 100644 index d74a3ef5665..00000000000 --- a/addons/stock/edi_stock_picking.py +++ /dev/null @@ -1,183 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2009 Tiny SPRL (). -# -# 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 . -# -############################################################################## - -from osv import fields, osv, orm -from base.ir import ir_edi -from tools.translate import _ - -class stock_picking(osv.osv, ir_edi.edi): - _inherit = 'stock.picking' - - def edi_export(self, cr, uid, records, edi_struct=None, context=None): - """Exports a supplier or customer invoice""" - edi_struct = { - - 'origin': True, - 'type': True, - 'stock_journal_id': True, - 'move_type': True, - 'date': True, - 'date_done': True, - 'move_lines': { - 'name': True, - 'date': True, - 'date_expected': True, - 'product_id': True, - 'product_qty': True, - 'product_uom': True, - 'location_id': True, - 'location_dest_id': True, - 'company_id': True, - }, - 'invoice_state': True, - 'address_id': True, - 'company_id': True, - } - partner_pool = self.pool.get('res.partner') - partner_address_pool = self.pool.get('res.partner.address') - company_address_dict = { - 'street': True, - 'street2': True, - 'zip': True, - 'city': True, - 'state_id': True, - 'country_id': True, - 'email': True, - 'phone': True, - - } - edi_doc_list = [] - for picking in records: - # Get EDI doc based on struct. The result will also contain all metadata fields and attachments. - edi_doc = super(stock_picking,self).edi_export(cr, uid, [picking], edi_struct, context) - if not edi_doc: - continue - edi_doc = edi_doc[0] - - # Add company info and address - res = partner_pool.address_get(cr, uid, [picking.company_id.partner_id.id], ['contact', 'picking']) - contact_addr_id = res['contact'] - invoice_addr_id = res['picking'] - - address = partner_address_pool.browse(cr, uid, invoice_addr_id, context=context) - edi_company_address_dict = {} - for key, value in company_address_dict.items(): - if not value: - continue - address_rec = getattr(address, key) - if not address_rec: - continue - if key.endswith('_id'): - address_rec = self.edi_m2o(cr, uid, address_rec, context=context) - edi_company_address_dict[key] = address_rec - - edi_doc.update({ - 'company_address': edi_company_address_dict, - #'company_logo': inv_comp.logo,#TODO - #'paid': inv_comp.paid, #TODO - }) - edi_doc_list.append(edi_doc) - return edi_doc_list - - def edi_import(self, cr, uid, edi_document, context=None): - - partner_pool = self.pool.get('res.partner') - partner_address_pool = self.pool.get('res.partner.address') - model_data_pool = self.pool.get('ir.model.data') - product_pool = self.pool.get('product.product') - product_categ_pool = self.pool.get('product.category') - company_pool = self.pool.get('res.company') - country_pool = self.pool.get('res.country') - state_pool = self.pool.get('res.country.state') - account_journal_pool = self.pool.get('account.journal') - invoice_line_pool = self.pool.get('account.invoice.line') - account_pool = self.pool.get('account.account') - tax_id = [] - account_id = [] - partner_id = None - company_id = None - if context is None: - context = {} - - # 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 - edi_company_address = edi_document['company_address'] - - company_name = edi_document['company_id'][1] - shipping_type = edi_document['type'] - state_id = edi_company_address.get('state_id', False) - state_name = state_id and state_id[1] - country_id = edi_company_address.get('country_id', False) - country_name = country_id and country_id[1] - - country_id = country_name and self.edi_import_relation(cr, uid, 'res.country', country_name, context=context) or False - state_id = state_name and self.edi_import_relation(cr, uid, 'res.country.state', state_name, - values={'country_id': country_id, 'code': state_name}, context=context) or False - address_value = { - 'street': edi_company_address.get('street', False), - 'street2': edi_company_address.get('street2', False), - 'zip': edi_company_address.get('zip', False), - 'city': edi_company_address.get('city', False), - 'state_id': state_id, - 'country_id': country_id, - 'email': edi_company_address.get('email', False), - 'phone': edi_company_address.get('phone', False), - - } - - - partner_value = {'name': company_name} - partner_id = partner_pool.search(cr, uid, [('name','=',company_name)]) - if len(partner_id): - partner_id = partner_pool.browse(cr, uid, partner_id[0], context=context) - address_id = partner_id.address[0].id - else: - partner_id = partner_pool.create(cr, uid, partner_value, context=context) - address_value.update({'partner_id': partner_id}) - address_id = partner_address_pool.create(cr, uid, address_value, context=context) - partner_address = partner_address_pool.browse(cr, uid, address_id, context=context) - edi_document.update({'address_id': self.edi_m2o(cr, uid, partner_address, context=context)}) - - - - - # change type: out'<->'in for shipping - shipping_type = shipping_type.startswith('in') and shipping_type.replace('in','out') or shipping_type.replace('out','in') - edi_document['type'] = shipping_type - company_id = edi_document['company_id'] - #print ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",edi_document['move_lines'] - edi_document.update({'company_id': edi_document['move_lines'][0]['company_id']}) - edi_document['move_lines'][0].update({'company_id':company_id}) - - del edi_document['company_address'] - - - - return super(stock_picking,self).edi_import(cr, uid, edi_document, context=context) - -stock_picking() - -class stock_move(osv.osv, ir_edi.edi): - _inherit='stock.move' - -stock_move() - From e1a12a3c434007bdc55f7854a867c039854f2294 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Fri, 15 Jul 2011 14:13:43 +0530 Subject: [PATCH 018/132] [FIX] account.edi_invoice: export value of reconciled field bzr revid: hmo@tinyerp.com-20110715084343-135d1hc6057j1sb4 --- addons/account/edi_invoice.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/account/edi_invoice.py b/addons/account/edi_invoice.py index 7c69b52b879..786fbb0b018 100644 --- a/addons/account/edi_invoice.py +++ b/addons/account/edi_invoice.py @@ -39,6 +39,7 @@ class account_invoice(osv.osv, ir_edi.edi): 'amount_untaxed': True, 'amount_tax': True, 'amount_total': True, + 'reconciled': True, 'date_invoice': True, 'date_due': True, 'partner_id': True, From a5d8abd4ada1e9f436755b66face4ba20e3b597f Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Fri, 15 Jul 2011 16:29:13 +0530 Subject: [PATCH 019/132] [FIX] account.edi_invoice: import correct address of partner bzr revid: hmo@tinyerp.com-20110715105913-kzcidgprxczq3mao --- addons/account/edi_invoice.py | 3 +++ addons/account/edi_invoice_action_data.xml | 7 +++---- addons/email_template/email_template_scheduler_data.xml | 8 -------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/addons/account/edi_invoice.py b/addons/account/edi_invoice.py index 786fbb0b018..22d1c793f22 100644 --- a/addons/account/edi_invoice.py +++ b/addons/account/edi_invoice.py @@ -196,6 +196,9 @@ class account_invoice(osv.osv, ir_edi.edi): partner = partner_pool.browse(cr, uid, partner_id, context=context) edi_document['partner_id'] = self.edi_m2o(cr, uid, partner, context=context) + partner_address = partner_address_pool.browse(cr, uid, address_id, context=context) + edi_document['address_invoice_id'] = self.edi_m2o(cr, uid, partner_address, context=context) + # change type: out_invoice'<->'in_invoice','out_refund'<->'in_refund' invoice_type = invoice_type.startswith('in_') and invoice_type.replace('in_','out_') or invoice_type.replace('out_','in_') edi_document['type'] = invoice_type diff --git a/addons/account/edi_invoice_action_data.xml b/addons/account/edi_invoice_action_data.xml index 75e814216e7..af778e2abe0 100644 --- a/addons/account/edi_invoice_action_data.xml +++ b/addons/account/edi_invoice_action_data.xml @@ -26,13 +26,11 @@ if object.partner_id.opt_out: self.pool.get('email.template').generate_mail(cr, - ${object.company_id.name} - Invoice ${object.number} ${object.address_invoice_id.email} True - -Hello ${object.partner_id.name}, + Hello ${object.partner_id.name}, We just registered the invoice ${object.number} for ${object.amount_total} ${object.currency_id.name} for your company ${object.company_id.name}. You can click on the following link to preview, print and pay this invoice: @@ -41,7 +39,8 @@ You can click on the following link to preview, print and pay this invoice: Please note that this invoice has to be paid before ${object.date_due} -Regards, +Regards, + mako Mail Template of Invoice For EDI Document account.invoice diff --git a/addons/email_template/email_template_scheduler_data.xml b/addons/email_template/email_template_scheduler_data.xml index 910aa8572e6..45a9be04ac7 100644 --- a/addons/email_template/email_template_scheduler_data.xml +++ b/addons/email_template/email_template_scheduler_data.xml @@ -1,14 +1,6 @@ - - - Default EMail Template Account - test@localhost - draft - - - Email Template scheduler From 1e27a50175223eb70cfbe30756125e57119a2595 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Fri, 15 Jul 2011 17:12:12 +0530 Subject: [PATCH 020/132] [FIX] account.edi_invoice: correct email template bzr revid: hmo@tinyerp.com-20110715114212-bh2kv0ev03dpdm6d --- addons/account/edi_invoice_action_data.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/account/edi_invoice_action_data.xml b/addons/account/edi_invoice_action_data.xml index af778e2abe0..4cf3de86489 100644 --- a/addons/account/edi_invoice_action_data.xml +++ b/addons/account/edi_invoice_action_data.xml @@ -30,14 +30,14 @@ if object.partner_id.opt_out: self.pool.get('email.template').generate_mail(cr, ${object.address_invoice_id.email} True - Hello ${object.partner_id.name}, + Hello ${object.address_invoice_id.name}, -We just registered the invoice ${object.number} for ${object.amount_total} ${object.currency_id.name} for your company ${object.company_id.name}. +We just registered the invoice ${object.number} for ${object.amount_total} ${object.currency_id.name} for your company ${object.partner_id.name}. You can click on the following link to preview, print and pay this invoice: ${object._context.get('edi_web_url_view')} -Please note that this invoice has to be paid before ${object.date_due} +Please note that this invoice has to be paid before ${object.date_due or ''} Regards, From 2357eb562e96c563793cfd4b1c34f9bfe87520dc Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 18 Aug 2011 17:36:04 +0200 Subject: [PATCH 021/132] [FIX] document_ftp: avoid calling res.users.read() with too few arguments (it's broken, pending fix) bzr revid: odo@openerp.com-20110818153604-gefze43wp1u63j02 --- addons/document_ftp/test_easyftp.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/addons/document_ftp/test_easyftp.py b/addons/document_ftp/test_easyftp.py index ac2cd722478..74890b1a333 100644 --- a/addons/document_ftp/test_easyftp.py +++ b/addons/document_ftp/test_easyftp.py @@ -36,13 +36,13 @@ def get_plain_ftp(timeout=10.0): def get_ftp_login(cr, uid, ormobj): ftp = get_plain_ftp() - user = ormobj.pool.get('res.users').read(cr, uid, uid) - passwd = user.get('password','') + user = ormobj.pool.get('res.users').browse(cr, uid, uid) + passwd = user.password or '' if passwd.startswith("$1$"): - # md5 by base crypt. We cannot decode, wild guess + # md5 by base crypt. We cannot decode, wild guess # that passwd = login - passwd = user.get('login', '') - ftp.login(user.get('login',''), passwd) + passwd = user.login + ftp.login(user.login, passwd) ftp.cwd("/" + cr.dbname) return ftp From b46c2554467b719fdef76c3a14a79878919f10bb Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Fri, 26 Aug 2011 17:55:19 +0530 Subject: [PATCH 022/132] [FIX] account: email template of account invoice bzr revid: hmo@tinyerp.com-20110826122519-3bk1phpg3jtw5mfn --- addons/account/edi_invoice_action_data.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/addons/account/edi_invoice_action_data.xml b/addons/account/edi_invoice_action_data.xml index 95633aea21b..dc6cca4fade 100644 --- a/addons/account/edi_invoice_action_data.xml +++ b/addons/account/edi_invoice_action_data.xml @@ -3,7 +3,7 @@ - context.update({'edi_web_url_view': '%s/edi/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) + context.update({'edi_web_invoice_url_view': '%s/edi/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) if not object.partner_id.opt_out: self.pool.get('email.template').generate_mail(cr, uid, self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'email_template_edi_invoice')[1], @@ -34,13 +34,13 @@ if not object.partner_id.opt_out: self.pool.get('email.template').generate_mail( <p> Hello ${object.address_invoice_id.name and ' ' or ''},</p> <p> You can click on the following link to preview, print and pay invoice: - <a href="${object._context.get('edi_web_url_view')}">${object._context.get('edi_web_url_view')}</a> + <a href="${object._context.get('edi_web_invoice_url_view')}">$${object._context.get('edi_web_invoice_url_view')} </a> </p> <p style="border-left: 1px solid #8e0000; margin-left: 30px;"> <strong>REFERENCES</strong><br /> Invoice number: <strong>${object.number}</strong><br /> Invoice amount: <strong>${object.amount_total} ${object.currency_id.name}</strong><br /> Invoice date: ${object.date_invoice or 'n/a'}<br /> Order reference: ${object.origin or 'n/a'}<br /> Your contact: <a href="mailto:${object.user_id.user_email or ''}?subject=Invoice%20${object.number}">${object.user_id.name}</a></p> -${object.company_id.paypal_account and '<p>It is possible to pay with Paypal: <br/> <a href=/"https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business='+object.company_id.paypal_account+'&item_name=OpenERP%20Invoice%20'+object.number and object.number.replace('/','%2f') or ''+'&invoice='+object.number and object.number.replace('/','%2f') or ''+'&amount='+object.amount_total+'&currency_code='+object.currency_id.name+'&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_'+object.currency_id.name+'/"><img src=/"https://www.paypalobjects.com/en_US/i/btn/btn_paynowCC_LG.gif/" style=/"margin-left: 100px; border: 0px; padding: 1px; text-decoration: none;/"/></a> </p>'} +${object.company_id.paypal_account and "<p>It is possible to pay with Paypal: <br/> <a href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Invoice%%20%s&invoice=%s&amount=%s&currency_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s\"><img src=\"https://www.paypalobjects.com/en_US/i/btn/btn_paynowCC_LG.gif\" style=\"margin-left: 100px; border: 0px; padding: 1px; text-decoration: none;\"/></a> </p>"%(object.company_id.paypal_account, object.number and object.number.replace('/','%2f') or '', object.number and object.number.replace('/','%2f') or '', object.amount_total, object.currency_id.name, object.currency_id.name) or ''} <p> If you have any question, do not hesitate to reply directly to this e-mail.</p> <p> Thank you for choosing OpenERP!<br /> </p> <div style="width: 375px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; overflow-x: hidden; overflow-y: hidden; zoom: 1; background-image: url(http://www.openerp.com/sites/default/files/red_gradient_bg.png); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(142, 0, 0); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-bottom-right-radius: 0px 0px; border-bottom-left-radius: 0px 0px; background-position: 0% 0%; background-repeat: repeat no-repeat; "> <h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 9px; padding-right: 14px; padding-bottom: 9px; padding-left: 14px; font-size: 12px; font-weight: normal; font-style: normal; color: rgb(255, 255, 255); "> <strong>${object.company_id.name}</strong></h3> </div> <div style="width: 347px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 12px; padding-right: 14px; padding-bottom: 12px; padding-left: 14px; overflow-x: hidden; overflow-y: hidden; zoom: 1; line-height: 16px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(242, 242, 242); "> <div> Contact:<a href="mailto:${object.user_id.user_email or ''}?subject=Invoice%20${object.number}">${object.user_id.name}</a></div> <div> </div> </div> </div> <p> </p> @@ -49,15 +49,15 @@ Hello ${object.address_invoice_id.name and ' ' or ''}, You can click on the following link to preview, print and pay invoice: - ${object._context.get('edi_web_url_view')} + ${object._context.get('edi_web_invoice_url_view') or 'n/a'} Invoice Number: *${object.number}* Amount: *${object.amount_total} ${object.currency_id.name}* Invoice date: ${object.date_invoice or 'n/a'} Order reference: ${object.origin or 'n/a'} -Your contact: ${object.user_id.name} <${object.user_id.user_email or ''}> +Your contact: ${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''} -${object.company_id.paypal_account and 'It is possible to pay with Paypal: https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business='+object.company_id.paypal_account+'&item_name=OpenERP%20Invoice%20'+object.number and object.number.replace('/','%2f') or ''+'&invoice='+object.number and object.number.replace('/','%2f') or ''+'&amount='+object.amount_total+'&currency_code='+object.currency_id.name+'&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_'+object.currency_id.name} +${object.company_id.paypal_account and "It is possible to pay with Paypal: https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Invoice%%20%s&invoice=%s&amount=%s&currency_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s"%(object.company_id.paypal_account, object.number and object.number.replace('/','%2f') or '', object.number and object.number.replace('/','%2f') or '', object.amount_total, object.currency_id.name, object.currency_id.name) or ''} If you have any question, do not hesitate to reply directly to this e-mail. @@ -65,7 +65,7 @@ Thank you for choosing our service! -- ${object.company_id.name} -Contact: ${object.user_id.name} <${object.user_id.user_email or ''}> +Contact: ${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''} mako Mail Template of Invoice For EDI Document From 333b5aa3e2bdcfdae54f2fcc9e9be3d0574dbd85 Mon Sep 17 00:00:00 2001 From: "Hardik Ansodariy (OpenERP)" Date: Fri, 26 Aug 2011 18:39:29 +0530 Subject: [PATCH 023/132] [IMP] edi sale order with email template bzr revid: han@tinyerp.com-20110826130929-q0n8o9gdy8a6ssf0 --- addons/sale/__init__.py | 2 +- addons/sale/__openerp__.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/sale/__init__.py b/addons/sale/__init__.py index 3c7af56e808..6361fea3a0b 100644 --- a/addons/sale/__init__.py +++ b/addons/sale/__init__.py @@ -29,5 +29,5 @@ import sale_installer import wizard import report import company - +import edi_sale_order # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale/__openerp__.py b/addons/sale/__openerp__.py index c983edc881e..4dccadc34e2 100644 --- a/addons/sale/__openerp__.py +++ b/addons/sale/__openerp__.py @@ -85,6 +85,7 @@ Dashboard for Sales Manager that includes: 'stock_view.xml', 'board_sale_view.xml', 'process/sale_process.xml', + 'edi_sale_order_data.xml', ], 'demo_xml': ['sale_demo.xml'], 'test': [ From 1ed41c0578aa05ef67709a2d9520a8eae3b25a3c Mon Sep 17 00:00:00 2001 From: "Hardik Ansodariy (OpenERP)" Date: Fri, 26 Aug 2011 18:40:37 +0530 Subject: [PATCH 024/132] [imp] edi sale order bzr revid: han@tinyerp.com-20110826131037-fr63ylzjd8ev1l5j --- addons/sale/edi_sale_order.py | 195 ++++++++++++++++++++++++++++ addons/sale/edi_sale_order_data.xml | 51 ++++++++ 2 files changed, 246 insertions(+) create mode 100644 addons/sale/edi_sale_order.py create mode 100644 addons/sale/edi_sale_order_data.xml diff --git a/addons/sale/edi_sale_order.py b/addons/sale/edi_sale_order.py new file mode 100644 index 00000000000..266727c0ae5 --- /dev/null +++ b/addons/sale/edi_sale_order.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). +# +# 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 . +# +############################################################################## + +from osv import fields, osv, orm +from base.ir import ir_edi +from tools.translate import _ +from datetime import date +class sale_order(osv.osv, ir_edi.edi): + _inherit = 'sale.order' + + def edi_export(self, cr, uid, records, edi_struct=None, context=None): + """Exports a Sale order""" + edi_struct = { + 'name': True, + 'shop_id': True, + 'origin': True, + 'amount_total': True, + 'date_order': True, + 'create_date': True, + 'date_confirm': True, + 'partner_id': True, + 'partner_invoice_id': True, + 'partner_order_id': True, + 'partner_shipping_id': True, + 'incoterm': True, + 'picking_policy': True, + 'order_policy': True, + 'pricelist_id': True, + 'project_id': True, + 'invoice_quantity': True, + 'order_line': { + 'name': True, + 'sequence': True, + 'product_id': True, + 'invoiced': True, + 'procurement_id': True, + 'price_unit': True, + 'type': True, + 'price_subtotal': True, + 'tax_id': True, + 'address_allotment_id': True, + 'product_uom': True, + 'product_uom_qty': True, + 'product_uos': True, + 'notes': True, + + }, + 'shipped': True, + } + partner_pool = self.pool.get('res.partner') + partner_address_pool = self.pool.get('res.partner.address') + company_address_dict = { + 'street': True, + 'street2': True, + 'zip': True, + 'city': True, + 'state_id': True, + 'country_id': True, + 'email': True, + 'phone': True, + + } + edi_doc_list = [] + for order in records: + # Get EDI doc based on struct. The result will also contain all metadata fields and attachments. + edi_doc = super(sale_order,self).edi_export(cr, uid, [order], edi_struct, context) + if not edi_doc: + continue + edi_doc = edi_doc[0] + + # Add company info and address + res = partner_pool.address_get(cr, uid, [order.shop_id.company_id.partner_id.id], ['contact', 'order']) + contact_addr_id = res['contact'] + invoice_addr_id = res['order'] + + address = partner_address_pool.browse(cr, uid, invoice_addr_id, context=context) + edi_company_address_dict = {} + for key, value in company_address_dict.items(): + if not value: + continue + address_rec = getattr(address, key) + if not address_rec: + continue + if key.endswith('_id'): + address_rec = self.edi_m2o(cr, uid, address_rec, context=context) + + edi_company_address_dict[key] = address_rec + + edi_doc.update({ + 'company_address': edi_company_address_dict, + #'company_logo': inv_comp.logo,#TODO + #'paid': inv_comp.paid, #TODO + }) + edi_doc_list.append(edi_doc) + return edi_doc_list + + def edi_import(self, cr, uid, edi_document, context=None): + + partner_pool = self.pool.get('res.partner') + partner_address_pool = self.pool.get('res.partner.address') + model_data_pool = self.pool.get('ir.model.data') + product_pool = self.pool.get('product.product') + product_categ_pool = self.pool.get('product.category') + company_pool = self.pool.get('res.company') + country_pool = self.pool.get('res.country') + state_pool = self.pool.get('res.country.state') + + tax_id = [] + account_id = [] + partner_id = None + company_id = None + if context is None: + context = {} + + # 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 + edi_company_address = edi_document['company_address'] + edi_partner_id = edi_document['partner_id'] + company_name = edi_document['company_id'][1] + state_id = edi_company_address.get('state_id', False) + state_name = state_id and state_id[1] + country_id = edi_company_address.get('country_id', False) + country_name = country_id and country_id[1] + + country_id = country_name and self.edi_import_relation(cr, uid, 'res.country', country_name, context=context) or False + state_id = state_name and self.edi_import_relation(cr, uid, 'res.country.state', state_name, + values={'country_id': country_id, 'code': state_name}, context=context) or False + address_value = { + 'street': edi_company_address.get('street', False), + 'street2': edi_company_address.get('street2', False), + 'zip': edi_company_address.get('zip', False), + 'city': edi_company_address.get('city', False), + 'state_id': state_id, + 'country_id': country_id, + 'email': edi_company_address.get('email', False), + 'phone': edi_company_address.get('phone', False), + + } + + partner_value = {'name': company_name} + partner_value.update({'customer': True, 'supplier': False}) + + partner_id = partner_pool.create(cr, uid, partner_value, context=context) + address_value.update({'partner_id': partner_id}) + address_id = partner_address_pool.create(cr, uid, address_value, context=context) + partner_address = partner_address_pool.browse(cr, uid, [address_id], context=context) + partner_address_id = self.edi_o2m(cr, uid, partner_address, context=context) + partner = partner_pool.browse(cr, uid, partner_id, context=context) + edi_document['partner_id'] = self.edi_m2o(cr, uid, partner, context=context) + edi_document.update({ + 'partner_invoice_id': partner_address_id, + 'partner_order_id': partner_address_id, + 'partner_shipping_id': partner_address_id, + #'product_uom_qty': edi_document['order_line'][0]['product_qty'], + 'delay': 10, + }) + # all fields are converted for sale order import so unnecessary fields are deleted + del edi_document['order_line'][0]['date_planned'] + del edi_document['order_line'][0]['product_qty'] + del edi_document['date_approve'] + del edi_document['validator'] + + del edi_document['location_id'] + del edi_document['partner_address_id'] + del edi_document['company_address'] + del edi_document['company_id'] + del edi_document['warehouse_id'] + return super(sale_order,self).edi_import(cr, uid, edi_document, context=context) + +sale_order() + +class sale_order_line(osv.osv, ir_edi.edi): + _inherit='sale.order.line' + +sale_order_line() +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale/edi_sale_order_data.xml b/addons/sale/edi_sale_order_data.xml new file mode 100644 index 00000000000..70b6adae397 --- /dev/null +++ b/addons/sale/edi_sale_order_data.xml @@ -0,0 +1,51 @@ + + + + + + context.update({'edi_web_url_view': '%s/edi/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) +if not object.partner_id.opt_out: self.pool.get('email.template').generate_mail(cr, + uid, + self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sale', 'email_template_edi_sale')[1], + [object.id], + context=context) + + code + ir.actions.server + + True + EDI Document - Sale Order + + + + + + + + + + + + ${object.company_id.name} - Sale Order ${object.name} + ${object.partner_invoice_id.email} + + True + +Hello ${object.partner_invoice_id.name}, + +We just registered the sale order ${object.name} for ${object.amount_total} for your company ${object.partner_id.name}. +You can click on the following link to preview. + + ${object._context.get('edi_web_url_view')} + + confirmed on ${object.date_confirm} + +Regards, + + mako + Mail Template of Sale Order For EDI Document + sale.order + ${object.user_id.email or ''} + + + From 39b40d789a2d23f985923447a8ce3d9f55b8c6f8 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Mon, 29 Aug 2011 13:09:56 +0530 Subject: [PATCH 025/132] [IMP] account: email template of account invoice bzr revid: hmo@tinyerp.com-20110829073956-vabkylrsvnklb8hw --- addons/account/edi_invoice_action_data.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/account/edi_invoice_action_data.xml b/addons/account/edi_invoice_action_data.xml index dc6cca4fade..fb89e585195 100644 --- a/addons/account/edi_invoice_action_data.xml +++ b/addons/account/edi_invoice_action_data.xml @@ -33,8 +33,8 @@ if not object.partner_id.opt_out: self.pool.get('email.template').generate_mail( <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 ''},</p> -<p> You can click on the following link to preview, print and pay invoice: - <a href="${object._context.get('edi_web_invoice_url_view')}">$${object._context.get('edi_web_invoice_url_view')} </a> +<p> You can click on the following link to preview, print and pay invoice: <br/> + <a href="${object._context.get('edi_web_invoice_url_view')}">${object._context.get('edi_web_invoice_url_view')} </a> </p> From 37263ec7accdb62e33f686c566ac4db81bdf46c3 Mon Sep 17 00:00:00 2001 From: "Hardik Ansodariy (OpenERP)" Date: Mon, 29 Aug 2011 15:51:27 +0530 Subject: [PATCH 026/132] [merge] edi sale order with yml bzr revid: han@tinyerp.com-20110829102127-nkr1muz2e1jsu6xh --- addons/purchase/__init__.py | 1 + addons/sale/__openerp__.py | 1 + addons/sale/edi_sale_order.py | 4 +-- addons/sale/test/edi_sale_order.yml | 51 +++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 addons/sale/test/edi_sale_order.yml diff --git a/addons/purchase/__init__.py b/addons/purchase/__init__.py index 301e32ebca9..87e13840e0d 100644 --- a/addons/purchase/__init__.py +++ b/addons/purchase/__init__.py @@ -26,6 +26,7 @@ import wizard import report import stock import company +import edi_purchase_order # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale/__openerp__.py b/addons/sale/__openerp__.py index 4dccadc34e2..0e3ec9ed075 100644 --- a/addons/sale/__openerp__.py +++ b/addons/sale/__openerp__.py @@ -100,6 +100,7 @@ Dashboard for Sales Manager that includes: 'test/invoice_on_ordered_qty.yml', 'test/invoice_on_shipped_qty.yml', 'test/sale_report.yml', + 'test/edi_sale_order.yml', ], 'installable': True, 'active': False, diff --git a/addons/sale/edi_sale_order.py b/addons/sale/edi_sale_order.py index 266727c0ae5..ea1a56b1ed4 100644 --- a/addons/sale/edi_sale_order.py +++ b/addons/sale/edi_sale_order.py @@ -38,8 +38,6 @@ class sale_order(osv.osv, ir_edi.edi): 'date_confirm': True, 'partner_id': True, 'partner_invoice_id': True, - 'partner_order_id': True, - 'partner_shipping_id': True, 'incoterm': True, 'picking_policy': True, 'order_policy': True, @@ -163,7 +161,7 @@ class sale_order(osv.osv, ir_edi.edi): address_value.update({'partner_id': partner_id}) address_id = partner_address_pool.create(cr, uid, address_value, context=context) partner_address = partner_address_pool.browse(cr, uid, [address_id], context=context) - partner_address_id = self.edi_o2m(cr, uid, partner_address, context=context) + partner_address_id = self.edi_o2m(cr, uid, [partner_address], context=context) partner = partner_pool.browse(cr, uid, partner_id, context=context) edi_document['partner_id'] = self.edi_m2o(cr, uid, partner, context=context) edi_document.update({ diff --git a/addons/sale/test/edi_sale_order.yml b/addons/sale/test/edi_sale_order.yml new file mode 100644 index 00000000000..dba200f4cfa --- /dev/null +++ b/addons/sale/test/edi_sale_order.yml @@ -0,0 +1,51 @@ +- + I create a partner which is a my customer +- + !record {model: res.partner, id: res_partner_test22}: + name: Junjun wala + supplier: False + customer: True + opt_out: False +- + I create one Sale Order +- + !record {model: sale.order, id: sale_order_test}: + partner_id: res_partner_test22 + partner_invoice_id: base.res_partner_address_11 + partner_order_id: base.res_partner_address_11 + partner_shipping_id: base.res_partner_address_11 + pricelist_id: 1 + order_line: + - product_id: product.product_product_pc1 + product_uom_qty: 1.0 + product_uom: 1 + price_unit: 150.0 + name: 'basic pc' +- + I Open the sale order +- + !python {model: sale.order}: | + + orders = self.browse(cr, uid, ref("sale_order_test")) + import netsvc + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'sale.order',orders.id,'order_confirm', cr) + +- + I Tesing of EDI functionality. First I export Sale Order from my company than import that Order into customer company +- + !python {model: ir.edi.document}: | + import json + invoice_pool = self.pool.get('sale.order') + orders = invoice_pool.browse(cr, uid, ref("sale_order_test")) + + tokens = self.export_edi(cr, uid, [orders]) + assert tokens, 'Token is not generated' + document = self.get_document(cr, uid, tokens[0]) + document = json.loads(document) + document[0]["__model"] = "purchase.order" + document = json.dumps(document) + a = self.import_edi(cr, uid, edi_document = document) + assert a, 'Invoice is not imported' + + From 7d33fcfa66a383b963e7c46fbd768bb07444476a Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Mon, 29 Aug 2011 16:13:27 +0530 Subject: [PATCH 027/132] [IMP] develop a edi purchase order import and export bzr revid: aar@tinyerp.com-20110829104327-tikju8eodq6q6901 --- addons/purchase/__init__.py | 2 +- addons/purchase/__openerp__.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/purchase/__init__.py b/addons/purchase/__init__.py index 301e32ebca9..ddd572d8e81 100644 --- a/addons/purchase/__init__.py +++ b/addons/purchase/__init__.py @@ -26,6 +26,6 @@ import wizard import report import stock import company - +import edi_purchase_order # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/purchase/__openerp__.py b/addons/purchase/__openerp__.py index 975754dd5fe..f360d881cf3 100644 --- a/addons/purchase/__openerp__.py +++ b/addons/purchase/__openerp__.py @@ -58,6 +58,7 @@ Dashboard for purchase management that includes: 'process/purchase_process.xml', 'report/purchase_report_view.xml', 'board_purchase_view.xml', + 'edi_purchase_order_data.xml', ], 'test': [ 'test/purchase_from_order.yml', @@ -66,6 +67,7 @@ Dashboard for purchase management that includes: 'purchase_unit_test.xml', 'test/procurement_buy.yml', 'test/purchase_report.yml', + #'test/edi_purchase_order.yml', ], 'demo': ['purchase_demo.xml'], 'installable': True, From d6b7e8abb72acd533e511ef0127371133ab5aee7 Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Mon, 29 Aug 2011 16:14:21 +0530 Subject: [PATCH 028/132] [IMP] develop a edi purchase order import and export bzr revid: aar@tinyerp.com-20110829104421-6562nvcyulzl4o84 --- addons/purchase/edi_purchase_order.py | 190 ++++++++++++++++++++ addons/purchase/edi_purchase_order_data.xml | 51 ++++++ addons/purchase/test/edi_purchase_order.yml | 31 ++++ 3 files changed, 272 insertions(+) create mode 100644 addons/purchase/edi_purchase_order.py create mode 100644 addons/purchase/edi_purchase_order_data.xml create mode 100644 addons/purchase/test/edi_purchase_order.yml diff --git a/addons/purchase/edi_purchase_order.py b/addons/purchase/edi_purchase_order.py new file mode 100644 index 00000000000..1ea43afbb09 --- /dev/null +++ b/addons/purchase/edi_purchase_order.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). +# +# 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 . +# +############################################################################## + +from osv import fields, osv, orm +from base.ir import ir_edi +from tools.translate import _ +from datetime import date + +class purchase_order(osv.osv, ir_edi.edi): + _inherit = 'purchase.order' + + def edi_export(self, cr, uid, records, edi_struct=None, context=None): + """Exports a supplier or customer invoice""" + edi_struct = { + 'name': True, + 'origin': True, + 'date_order': True, + 'date_approve': True, + 'partner_id': True, + 'partner_address_id': True, + 'dest_address_id': True, + 'warehouse_id': True, + 'location_id': True, + 'pricelist_id': True, + 'validator' : True, + 'order_line': { + 'name': True, + 'product_qty': True, + 'date_planned': True, + 'taxes_id': True, + 'product_uom': True, + 'product_id': True, + 'move_dest_id': True, + 'price_unit': True, + 'order_id': True, + 'invoiced': True, + 'price_subtotal': True, + }, + 'invoice_ids': True, + 'shipped': True, + 'company_id': True, + } + partner_pool = self.pool.get('res.partner') + partner_address_pool = self.pool.get('res.partner.address') + company_address_dict = { + 'street': True, + 'street2': True, + 'zip': True, + 'city': True, + 'state_id': True, + 'country_id': True, + 'email': True, + 'phone': True, + + } + edi_doc_list = [] + for order in records: + # Get EDI doc based on struct. The result will also contain all metadata fields and attachments. + edi_doc = super(purchase_order,self).edi_export(cr, uid, [order], edi_struct, context) + if not edi_doc: + continue + edi_doc = edi_doc[0] + + # Add company info and address + res = partner_pool.address_get(cr, uid, [order.company_id.partner_id.id], ['contact', 'order']) + contact_addr_id = res['contact'] + invoice_addr_id = res['order'] + + address = partner_address_pool.browse(cr, uid, invoice_addr_id, context=context) + edi_company_address_dict = {} + for key, value in company_address_dict.items(): + if not value: + continue + address_rec = getattr(address, key) + if not address_rec: + continue + if key.endswith('_id'): + address_rec = self.edi_m2o(cr, uid, address_rec, context=context) + edi_company_address_dict[key] = address_rec + + edi_doc.update({ + 'company_address': edi_company_address_dict, + #'company_logo': inv_comp.logo,#TODO + #'paid': inv_comp.paid, #TODO + }) + edi_doc_list.append(edi_doc) + return edi_doc_list + + def edi_import(self, cr, uid, edi_document, context=None): + + partner_pool = self.pool.get('res.partner') + partner_address_pool = self.pool.get('res.partner.address') + model_data_pool = self.pool.get('ir.model.data') + product_pool = self.pool.get('product.product') + product_categ_pool = self.pool.get('product.category') + company_pool = self.pool.get('res.company') + country_pool = self.pool.get('res.country') + state_pool = self.pool.get('res.country.state') + account_journal_pool = self.pool.get('account.journal') + invoice_line_pool = self.pool.get('account.invoice.line') + account_pool = self.pool.get('account.account') + stock = self.pool.get('stock.location') + + tax_id = [] + account_id = [] + partner_id = None + company_id = None + if context is None: + context = {} + + # 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 + edi_company_address = edi_document['company_address'] + edi_partner_id = edi_document['partner_id'] + company_name = edi_document['shop_id'][1] + state_id = edi_company_address.get('state_id', False) + state_name = state_id and state_id[1] + country_id = edi_company_address.get('country_id', False) + country_name = country_id and country_id[1] + + country_id = country_name and self.edi_import_relation(cr, uid, 'res.country', country_name, context=context) or False + state_id = state_name and self.edi_import_relation(cr, uid, 'res.country.state', state_name, + values={'country_id': country_id, 'code': state_name}, context=context) or False + address_value = { + 'street': edi_company_address.get('street', False), + 'street2': edi_company_address.get('street2', False), + 'zip': edi_company_address.get('zip', False), + 'city': edi_company_address.get('city', False), + 'state_id': state_id, + 'country_id': country_id, + 'email': edi_company_address.get('email', False), + 'phone': edi_company_address.get('phone', False), + + } + partner_value = {'name': company_name} + partner_value.update({'customer': False, 'supplier': True}) + partner_id = partner_pool.create(cr, uid, partner_value, context=context) + address_value.update({'partner_id': partner_id}) + address_id = partner_address_pool.create(cr, uid, address_value, context=context) + partner_address = partner_address_pool.browse(cr, uid, address_id, context=context) + edi_document.update({'partner_address_id':self.edi_m2o(cr, uid, partner_address, context=context)}) + partner = partner_pool.browse(cr, uid, partner_id, context=context) + edi_document['partner_id'] = self.edi_m2o(cr, uid, partner, context=context) + location_id = stock.search(cr, uid,[('name','=','Stock')]) + location = stock.browse(cr, uid, location_id[0]) + edi_document.update({'dest_address_id': edi_document['partner_shipping_id'],'location_id': self.edi_m2o(cr, uid, location, context=context)}) + + for line in range(len(edi_document['order_line'])): + product_qty = edi_document['order_line'][line]['product_uom_qty'] + edi_document['order_line'][line].update({'product_qty': product_qty}) + + # all fields are converted for purchase order import so unnecessary fields are deleted + delete_key = ['sequence','procurement_id','product_uom_qty','company_address','shop_id','create_date','picking_policy','order_policy','partner_order_id','partner_shipping_id','invoice_quantity','partner_invoice_id','price_subtotal','date_confirm'] + for key in delete_key: + if edi_document.has_key(key): + del edi_document[key] + else: + for document in edi_document['order_line']: + if document.has_key(key): + del document[key] + + print edi_document + return super(purchase_order,self).edi_import(cr, uid, edi_document, context=context) + +purchase_order() + +class purchase_order_line(osv.osv, ir_edi.edi): + _inherit='purchase.order.line' + +purchase_order_line() +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/purchase/edi_purchase_order_data.xml b/addons/purchase/edi_purchase_order_data.xml new file mode 100644 index 00000000000..ab29119bed1 --- /dev/null +++ b/addons/purchase/edi_purchase_order_data.xml @@ -0,0 +1,51 @@ + + + + + + context.update({'edi_web_url_view': '%s/edi/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) +self.pool.get('email.template').generate_mail(cr, + uid, + self.pool.get('ir.model.data').get_object_reference(cr, uid, 'purchase', 'email_template_edi_purchase')[1], + [object.id], + context=context) + + code + ir.actions.server + + True + EDI Document - Purchase Order + + + + + + + + + + + + ${object.company_id.name} - Purchase Order ${object.name} + ${object.partner_address_id.email} + + True + +Hello ${object.partner_address_id.name}, + +We just registered the purchase order ${object.name} for ${object.amount_total} for your company ${object.partner_id.name}. +You can click on the following link to preview. + + ${object._context.get('edi_web_url_view')} + + Confirmed on ${object.date_approve} + +Regards, + + mako + Mail Template of Purchase Order For EDI Document + purchase.order + + + + diff --git a/addons/purchase/test/edi_purchase_order.yml b/addons/purchase/test/edi_purchase_order.yml new file mode 100644 index 00000000000..2901aeb5e9a --- /dev/null +++ b/addons/purchase/test/edi_purchase_order.yml @@ -0,0 +1,31 @@ +- + I Tesing of EDI functionality. First I export customer invoice from my company than import that invoice into customer company +- + !python {model: ir.edi.document}: | + import json + order_pool = self.pool.get('purchase.order') + purchase_id = order_pool.search(cr, uid, []) + orders = order_pool.browse(cr, uid, purchase_id) + + if type(orders).__name__ == 'list': + tokens = self.export_edi(cr, uid, orders) + for token in tokens: + document = self.get_document(cr, uid, token) + document = json.loads(document) + edi_doc = [] + for doc in document: + doc.update({'__model': 'sale.order'}) + edi_doc.append(doc) + a = self.import_edi(cr, uid, edi_document = json.dumps(edi_doc)) + else: + tokens = self.export_edi(cr, uid, orders) + for token in tokens: + document = self.get_document(cr, uid, token) + document = json.loads(document) + edi_doc = [] + for doc in document: + doc.update({'__model': 'sale.order'}) + edi_doc.append(doc) + a = self.import_edi(cr, uid, edi_document = json.dumps(edi_doc)) + + From 5ffef81c62fbe2bf2056d65119dadc4d30a2b0e3 Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Tue, 30 Aug 2011 11:04:53 +0530 Subject: [PATCH 029/132] [IMP] improve the edi purchase order import and export testing bzr revid: aar@tinyerp.com-20110830053453-ecsqohdm12kp6xao --- addons/purchase/__openerp__.py | 3 +- addons/purchase/edi_purchase_order.py | 4 +- addons/purchase/edi_purchase_order_data.xml | 2 +- addons/purchase/test/edi_purchase_order.yml | 71 +++++++++++++-------- 4 files changed, 47 insertions(+), 33 deletions(-) diff --git a/addons/purchase/__openerp__.py b/addons/purchase/__openerp__.py index f360d881cf3..21a9184cbb3 100644 --- a/addons/purchase/__openerp__.py +++ b/addons/purchase/__openerp__.py @@ -59,6 +59,7 @@ Dashboard for purchase management that includes: 'report/purchase_report_view.xml', 'board_purchase_view.xml', 'edi_purchase_order_data.xml', + ], 'test': [ 'test/purchase_from_order.yml', @@ -67,7 +68,7 @@ Dashboard for purchase management that includes: 'purchase_unit_test.xml', 'test/procurement_buy.yml', 'test/purchase_report.yml', - #'test/edi_purchase_order.yml', + 'test/edi_purchase_order.yml', ], 'demo': ['purchase_demo.xml'], 'installable': True, diff --git a/addons/purchase/edi_purchase_order.py b/addons/purchase/edi_purchase_order.py index 1ea43afbb09..8621e33f15b 100644 --- a/addons/purchase/edi_purchase_order.py +++ b/addons/purchase/edi_purchase_order.py @@ -118,14 +118,13 @@ class purchase_order(osv.osv, ir_edi.edi): invoice_line_pool = self.pool.get('account.invoice.line') account_pool = self.pool.get('account.account') stock = self.pool.get('stock.location') - tax_id = [] account_id = [] partner_id = None company_id = None if context is None: context = {} - + print edi_document # 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 @@ -178,7 +177,6 @@ class purchase_order(osv.osv, ir_edi.edi): if document.has_key(key): del document[key] - print edi_document return super(purchase_order,self).edi_import(cr, uid, edi_document, context=context) purchase_order() diff --git a/addons/purchase/edi_purchase_order_data.xml b/addons/purchase/edi_purchase_order_data.xml index ab29119bed1..937045d3380 100644 --- a/addons/purchase/edi_purchase_order_data.xml +++ b/addons/purchase/edi_purchase_order_data.xml @@ -45,7 +45,7 @@ Regards, mako Mail Template of Purchase Order For EDI Document purchase.order - + ${object.user_id.user_email or ''} diff --git a/addons/purchase/test/edi_purchase_order.yml b/addons/purchase/test/edi_purchase_order.yml index 2901aeb5e9a..e6ee0c76840 100644 --- a/addons/purchase/test/edi_purchase_order.yml +++ b/addons/purchase/test/edi_purchase_order.yml @@ -1,31 +1,46 @@ - - I Tesing of EDI functionality. First I export customer invoice from my company than import that invoice into customer company + I create a partner which is a my customer +- + !record {model: res.partner, id: res_partner_test20}: + name: jones white + supplier: False + customer: True + opt_out: False +- + I create one Purchase Order +- + !record {model: purchase.order, id: purchase_order_test}: + partner_id: res_partner_test22 + partner_address_id: base.res_partner_address_11 + location_id: stock.stock_location_3 + pricelist_id: 1 + order_line: + - product_id: product.product_product_pc1 + product_uom_qty: 1.0 + product_uom: 1 + price_unit: 150.0 + name: 'basic pc' +- + I Open the sale order +- + !python {model: purchase.order}: | + + orders = self.browse(cr, uid, ref("purchase_order_test")) + import netsvc + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'purchase.order',orders.id,'approved', cr) - !python {model: ir.edi.document}: | - import json - order_pool = self.pool.get('purchase.order') - purchase_id = order_pool.search(cr, uid, []) - orders = order_pool.browse(cr, uid, purchase_id) - - if type(orders).__name__ == 'list': - tokens = self.export_edi(cr, uid, orders) - for token in tokens: - document = self.get_document(cr, uid, token) - document = json.loads(document) - edi_doc = [] - for doc in document: - doc.update({'__model': 'sale.order'}) - edi_doc.append(doc) - a = self.import_edi(cr, uid, edi_document = json.dumps(edi_doc)) - else: - tokens = self.export_edi(cr, uid, orders) - for token in tokens: - document = self.get_document(cr, uid, token) - document = json.loads(document) - edi_doc = [] - for doc in document: - doc.update({'__model': 'sale.order'}) - edi_doc.append(doc) - a = self.import_edi(cr, uid, edi_document = json.dumps(edi_doc)) - - + order_pool = self.pool.get('purchase.order') + purchase_id = order_pool.search(cr, uid, ref("purchase_order_test")) + orders = order_pool.browse(cr, uid, purchase_id) + import json + + tokens = self.export_edi(cr, uid, [orders]) + assert tokens, 'Token is not generated' + document = self.get_document(cr, uid, tokens[0]) + document = json.loads(document) + document[0]["__model"] = "purchase.order" + document = json.dumps(document) + a = self.import_edi(cr, uid, edi_document = document) + From 78f10b2fe38236d6c6da452d4e2d3d00d72ef407 Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Tue, 30 Aug 2011 18:54:31 +0530 Subject: [PATCH 030/132] [IMP] improve the email template bzr revid: aar@tinyerp.com-20110830132431-4zsut2pz3fudcb5g --- addons/purchase/edi_purchase_order.py | 4 ++ addons/purchase/edi_purchase_order_data.xml | 45 ++++++++++++++++----- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/addons/purchase/edi_purchase_order.py b/addons/purchase/edi_purchase_order.py index 8621e33f15b..2d719300b8d 100644 --- a/addons/purchase/edi_purchase_order.py +++ b/addons/purchase/edi_purchase_order.py @@ -41,6 +41,9 @@ class purchase_order(osv.osv, ir_edi.edi): 'location_id': True, 'pricelist_id': True, 'validator' : True, + 'amount_tax': True, + 'amount_total': True, + 'amount_untaxed': True, 'order_line': { 'name': True, 'product_qty': True, @@ -102,6 +105,7 @@ class purchase_order(osv.osv, ir_edi.edi): #'paid': inv_comp.paid, #TODO }) edi_doc_list.append(edi_doc) + print "??????????????????????",edi_doc_list return edi_doc_list def edi_import(self, cr, uid, edi_document, context=None): diff --git a/addons/purchase/edi_purchase_order_data.xml b/addons/purchase/edi_purchase_order_data.xml index 937045d3380..7351afe4a3f 100644 --- a/addons/purchase/edi_purchase_order_data.xml +++ b/addons/purchase/edi_purchase_order_data.xml @@ -4,7 +4,7 @@ context.update({'edi_web_url_view': '%s/edi/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) -self.pool.get('email.template').generate_mail(cr, +if not object.partner_id.opt_out: self.pool.get('email.template').generate_mail(cr, uid, self.pool.get('ir.model.data').get_object_reference(cr, uid, 'purchase', 'email_template_edi_purchase')[1], [object.id], @@ -30,22 +30,47 @@ self.pool.get('email.template').generate_mail(cr, ${object.partner_address_id.email} True + +<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); "> + +<p> Hello ${object.partner_address_id.name and ' ' or ''},</p> +<p> You can click on the following link to preview, print and pay invoice: <br/> + <a href="${object._context.get('edi_web_url_view')}">${object._context.get('edi_web_url_view')} </a> +</p> + + +<p style="border-left: 1px solid #8e0000; margin-left: 30px;"> <strong>REFERENCES</strong><br /> Order number: <strong>${object.name}</strong><br /> Order amount: <strong>${object.amount_total} </strong><br /> Confirm date: ${object.date_approve or 'n/a'}<br /> Your contact: <a href="mailto:${object.validator.user_email or ''}?subject=Order%20${object.name}">${object.validator.name}</a></p> + +${object.company_id.paypal_account and "<p>It is possible to pay with Paypal: <br/> <a href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Invoice%%20%s&invoice=%s&amount=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s\"><img src=\"https://www.paypalobjects.com/en_US/i/btn/btn_paynowCC_LG.gif\" style=\"margin-left: 100px; border: 0px; padding: 1px; text-decoration: none;\"/></a> </p>"%(object.company_id.paypal_account, object.name and object.name.replace('/','%2f') or '', object.name and object.name.replace('/','%2f') or '', object.amount_total) or ''} + +<p> If you have any question, do not hesitate to reply directly to this e-mail.</p> <p> Thank you for choosing OpenERP!<br /> </p> <div style="width: 375px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; overflow-x: hidden; overflow-y: hidden; zoom: 1; background-image: url(http://www.openerp.com/sites/default/files/red_gradient_bg.png); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(142, 0, 0); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-bottom-right-radius: 0px 0px; border-bottom-left-radius: 0px 0px; background-position: 0% 0%; background-repeat: repeat no-repeat; "> <h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 9px; padding-right: 14px; padding-bottom: 9px; padding-left: 14px; font-size: 12px; font-weight: normal; font-style: normal; color: rgb(255, 255, 255); "> <strong>${object.company_id.name}</strong></h3> </div> <div style="width: 347px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 12px; padding-right: 14px; padding-bottom: 12px; padding-left: 14px; overflow-x: hidden; overflow-y: hidden; zoom: 1; line-height: 16px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(242, 242, 242); "> <div> Contact:<a href="mailto:${object.validator.user_email or ''}?subject=Order%20${object.name}">${object.validator.name}</a></div> <div> </div> </div> </div> <p> </p> + -Hello ${object.partner_address_id.name}, +Hello ${object.partner_address_id.name and ' ' or ''}, -We just registered the purchase order ${object.name} for ${object.amount_total} for your company ${object.partner_id.name}. -You can click on the following link to preview. +You can click on the following link to preview, print and pay invoice: - ${object._context.get('edi_web_url_view')} + ${object._context.get('edi_web_url_view') or 'n/a'} - Confirmed on ${object.date_approve} - -Regards, - +Order Number: *${object.name}* +Amount: *${object.amount_total}* +Approve date: ${object.date_approve or 'n/a'} +Your contact: ${object.validator.name} ${object.validator.user_email and '<%s>'%(object.validator.user_email) or ''} + +${object.company_id.paypal_account and "It is possible to pay with Paypal: https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Invoice%%20%s&invoice=%s&amount=%s&currency_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s"%(object.company_id.paypal_account, object.name and object.name.replace('/','%2f') or '', object.name and object.name.replace('/','%2f') or '', object.amount_total) or ''} + +If you have any question, do not hesitate to reply directly to this e-mail. + +Thank you for choosing our service! + +-- +${object.company_id.name} +Contact: ${object.validator.name} ${object.validator.user_email and '<%s>'%(object.validator.user_email) or ''} + mako Mail Template of Purchase Order For EDI Document purchase.order - ${object.user_id.user_email or ''} + ${object.validator.user_email or ''} From 3a581f8a27a1684218c3d98688f80b8cef5bfee9 Mon Sep 17 00:00:00 2001 From: "Amit Parmar (OpenERP)" Date: Wed, 7 Sep 2011 13:09:25 +0530 Subject: [PATCH 031/132] [IMP] Develop a purchase order import and export functionality bzr revid: aar@tinyerp.com-20110907073925-p7akjs01643z4m2b --- addons/email_template/email_template.py | 4 +- addons/purchase/__openerp__.py | 4 +- addons/purchase/edi_purchase_order.py | 7 +- addons/purchase/test/edi_purchase_order.yml | 7 +- addons/sale/__init__.py | 2 +- addons/sale/__openerp__.py | 22 +-- addons/sale/edi_sale_order.py | 187 ++++++++++++++++++++ addons/sale/edi_sale_order_data.xml | 76 ++++++++ addons/sale/test/edi_sale_order.yml | 51 ++++++ 9 files changed, 340 insertions(+), 20 deletions(-) create mode 100644 addons/sale/edi_sale_order.py create mode 100644 addons/sale/edi_sale_order_data.xml create mode 100644 addons/sale/test/edi_sale_order.yml diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index aabc211df11..57ba74a49d4 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -570,7 +570,8 @@ This is useful for CRM leads for example"), # determine name of sender, either it is specified in email_id or we # use the account name - email_id = from_account['email_id'].strip() + print "////////////////////////",from_account + email_id = from_account['email_id'] email_from = re.findall(r'([^ ,<@]+@[^> ,]+)', email_id)[0] if email_from != email_id: # we should keep it all, name is probably specified in the address @@ -670,6 +671,7 @@ This is useful for CRM leads for example"), if context is None: context = {} template = self.browse(cursor, user, template_id, context=context) + print ">>>>>>>>>>>><<<<<<<<<<<<<<<<",template if not template: raise Exception("The requested template could not be loaded") result = True diff --git a/addons/purchase/__openerp__.py b/addons/purchase/__openerp__.py index 21a9184cbb3..ae4eaa3c6e6 100644 --- a/addons/purchase/__openerp__.py +++ b/addons/purchase/__openerp__.py @@ -59,7 +59,7 @@ Dashboard for purchase management that includes: 'report/purchase_report_view.xml', 'board_purchase_view.xml', 'edi_purchase_order_data.xml', - + #'test/edi_purchase_order.yml', ], 'test': [ 'test/purchase_from_order.yml', @@ -68,7 +68,7 @@ Dashboard for purchase management that includes: 'purchase_unit_test.xml', 'test/procurement_buy.yml', 'test/purchase_report.yml', - 'test/edi_purchase_order.yml', + #'test/edi_purchase_order.yml', ], 'demo': ['purchase_demo.xml'], 'installable': True, diff --git a/addons/purchase/edi_purchase_order.py b/addons/purchase/edi_purchase_order.py index 2d719300b8d..605e29621b1 100644 --- a/addons/purchase/edi_purchase_order.py +++ b/addons/purchase/edi_purchase_order.py @@ -104,8 +104,9 @@ class purchase_order(osv.osv, ir_edi.edi): #'company_logo': inv_comp.logo,#TODO #'paid': inv_comp.paid, #TODO }) + edi_doc['__model'] = 'sale.order' edi_doc_list.append(edi_doc) - print "??????????????????????",edi_doc_list + return edi_doc_list def edi_import(self, cr, uid, edi_document, context=None): @@ -169,7 +170,7 @@ class purchase_order(osv.osv, ir_edi.edi): for line in range(len(edi_document['order_line'])): product_qty = edi_document['order_line'][line]['product_uom_qty'] - edi_document['order_line'][line].update({'product_qty': product_qty}) + edi_document['order_line'][line].update({'product_qty': product_qty,'taxes_id':edi_document['order_line'][line]['tax_id']}) # all fields are converted for purchase order import so unnecessary fields are deleted delete_key = ['sequence','procurement_id','product_uom_qty','company_address','shop_id','create_date','picking_policy','order_policy','partner_order_id','partner_shipping_id','invoice_quantity','partner_invoice_id','price_subtotal','date_confirm'] @@ -180,7 +181,7 @@ class purchase_order(osv.osv, ir_edi.edi): for document in edi_document['order_line']: if document.has_key(key): del document[key] - + print "in the edi_purchase import",edi_document return super(purchase_order,self).edi_import(cr, uid, edi_document, context=context) purchase_order() diff --git a/addons/purchase/test/edi_purchase_order.yml b/addons/purchase/test/edi_purchase_order.yml index e6ee0c76840..1fbc89db70a 100644 --- a/addons/purchase/test/edi_purchase_order.yml +++ b/addons/purchase/test/edi_purchase_order.yml @@ -10,16 +10,17 @@ I create one Purchase Order - !record {model: purchase.order, id: purchase_order_test}: - partner_id: res_partner_test22 + partner_id: res_partner_test20 partner_address_id: base.res_partner_address_11 location_id: stock.stock_location_3 pricelist_id: 1 order_line: - product_id: product.product_product_pc1 - product_uom_qty: 1.0 + product_qty: 1.0 product_uom: 1 price_unit: 150.0 name: 'basic pc' + date_planned: '2011-08-31' - I Open the sale order - @@ -40,7 +41,7 @@ assert tokens, 'Token is not generated' document = self.get_document(cr, uid, tokens[0]) document = json.loads(document) - document[0]["__model"] = "purchase.order" + document[0]["__model"] = "sale.order" document = json.dumps(document) a = self.import_edi(cr, uid, edi_document = document) diff --git a/addons/sale/__init__.py b/addons/sale/__init__.py index 3c7af56e808..6361fea3a0b 100644 --- a/addons/sale/__init__.py +++ b/addons/sale/__init__.py @@ -29,5 +29,5 @@ import sale_installer import wizard import report import company - +import edi_sale_order # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale/__openerp__.py b/addons/sale/__openerp__.py index c983edc881e..fa139377b40 100644 --- a/addons/sale/__openerp__.py +++ b/addons/sale/__openerp__.py @@ -85,20 +85,22 @@ Dashboard for Sales Manager that includes: 'stock_view.xml', 'board_sale_view.xml', 'process/sale_process.xml', + 'edi_sale_order_data.xml', ], 'demo_xml': ['sale_demo.xml'], 'test': [ 'test/data_test.yml', - 'test/manual_order_policy.yml', - 'test/prepaid_order_policy.yml', - 'test/picking_order_policy.yml', - 'test/postpaid_order_policy.yml', - 'test/advance_invoice.yml', - 'test/so_make_line_invoice.yml', - 'test/sale_procurement.yml', - 'test/invoice_on_ordered_qty.yml', - 'test/invoice_on_shipped_qty.yml', - 'test/sale_report.yml', + #'test/manual_order_policy.yml', + #'test/prepaid_order_policy.yml', + #'test/picking_order_policy.yml', + #'test/postpaid_order_policy.yml', + #'test/advance_invoice.yml', + #'test/so_make_line_invoice.yml', + #'test/sale_procurement.yml', + #'test/invoice_on_ordered_qty.yml', + #'test/invoice_on_shipped_qty.yml', + #'test/sale_report.yml', + #'test/edi_sale_order.yml', ], 'installable': True, 'active': False, diff --git a/addons/sale/edi_sale_order.py b/addons/sale/edi_sale_order.py new file mode 100644 index 00000000000..d33c4664992 --- /dev/null +++ b/addons/sale/edi_sale_order.py @@ -0,0 +1,187 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). +# +# 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 . +# +############################################################################## + +from osv import fields, osv, orm +from base.ir import ir_edi +from tools.translate import _ +from datetime import date +class sale_order(osv.osv, ir_edi.edi): + _inherit = 'sale.order' + + def edi_export(self, cr, uid, records, edi_struct=None, context=None): + """Exports a Sale order""" + edi_struct = { + 'name': True, + 'shop_id': True, + 'origin': True, + 'amount_total': True, + 'date_order': True, + 'date_confirm': True, + 'partner_id': True, + 'partner_invoice_id': True, + 'pricelist_id': True, + 'company_id': True, + 'amount_tax': True, + 'amount_total': True, + 'amount_untaxed': True, + 'order_line': { + 'name': True, + 'product_id': True, + 'procurement_id': True, + 'price_unit': True, + 'price_subtotal': True, + 'tax_id': True, + 'product_uom': True, + 'product_uom_qty': True, + 'product_uos': True, + + }, + 'shipped': True, + } + partner_pool = self.pool.get('res.partner') + partner_address_pool = self.pool.get('res.partner.address') + company_address_dict = { + 'street': True, + 'street2': True, + 'zip': True, + 'city': True, + 'state_id': True, + 'country_id': True, + 'email': True, + 'phone': True, + + } + edi_doc_list = [] + for order in records: + # Get EDI doc based on struct. The result will also contain all metadata fields and attachments. + edi_doc = super(sale_order,self).edi_export(cr, uid, [order], edi_struct, context) + if not edi_doc: + continue + edi_doc = edi_doc[0] + + # Add company info and address + res = partner_pool.address_get(cr, uid, [order.shop_id.company_id.partner_id.id], ['contact', 'order']) + contact_addr_id = res['contact'] + invoice_addr_id = res['order'] + + address = partner_address_pool.browse(cr, uid, invoice_addr_id, context=context) + edi_company_address_dict = {} + for key, value in company_address_dict.items(): + if not value: + continue + address_rec = getattr(address, key) + if not address_rec: + continue + if key.endswith('_id'): + address_rec = self.edi_m2o(cr, uid, address_rec, context=context) + + edi_company_address_dict[key] = address_rec + + edi_doc.update({ + 'company_address': edi_company_address_dict, + #'company_logo': inv_comp.logo,#TODO + #'paid': inv_comp.paid, #TODO + }) + edi_doc['__model'] = 'purchase.order' + edi_doc_list.append(edi_doc) + return edi_doc_list + + def edi_import(self, cr, uid, edi_document, context=None): + partner_pool = self.pool.get('res.partner') + partner_address_pool = self.pool.get('res.partner.address') + model_data_pool = self.pool.get('ir.model.data') + product_pool = self.pool.get('product.product') + product_categ_pool = self.pool.get('product.category') + company_pool = self.pool.get('res.company') + country_pool = self.pool.get('res.country') + state_pool = self.pool.get('res.country.state') + + tax_id = [] + account_id = [] + partner_id = None + company_id = None + if context is None: + context = {} + + # 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 + edi_company_address = edi_document['company_address'] + edi_partner_id = edi_document['partner_id'] + company_name = edi_document['company_id'][1] + state_id = edi_company_address.get('state_id', False) + state_name = state_id and state_id[1] + country_id = edi_company_address.get('country_id', False) + country_name = country_id and country_id[1] + + country_id = country_name and self.edi_import_relation(cr, uid, 'res.country', country_name, context=context) or False + state_id = state_name and self.edi_import_relation(cr, uid, 'res.country.state', state_name, + values={'country_id': country_id, 'code': state_name}, context=context) or False + address_value = { + 'street': edi_company_address.get('street', False), + 'street2': edi_company_address.get('street2', False), + 'zip': edi_company_address.get('zip', False), + 'city': edi_company_address.get('city', False), + 'state_id': state_id, + 'country_id': country_id, + 'email': edi_company_address.get('email', False), + 'phone': edi_company_address.get('phone', False), + + } + + partner_value = {'name': company_name} + partner_value.update({'customer': True, 'supplier': False}) + + partner_id = partner_pool.create(cr, uid, partner_value, context=context) + address_value.update({'partner_id': partner_id}) + address_id = partner_address_pool.create(cr, uid, address_value, context=context) + partner_address = partner_address_pool.browse(cr, uid, address_id, context=context) + partner_address_id = self.edi_m2o(cr, uid, partner_address, context=context) + partner = partner_pool.browse(cr, uid, partner_id, context=context) + edi_document['partner_id'] = self.edi_m2o(cr, uid, partner, context=context) + edi_document.update({ + 'partner_invoice_id': partner_address_id, + 'partner_order_id': partner_address_id, + 'partner_shipping_id': partner_address_id, + #'product_uom_qty': edi_document['order_line'][0]['product_qty'], + 'delay': 10, + }) + + # all fields are converted for sale order import so unnecessary fields are deleted + for i in range(len(edi_document['order_line'])): + edi_document['order_line'][i].update({'tax_id':edi_document['order_line'][i]['taxes_id']}) + delete_key = ['date_planned','product_qty','date_approve','validator','location_id','partner_address_id','company_address','company_id','warehouse_id','taxes_id'] + for key in delete_key: + if edi_document.has_key(key): + del edi_document[key] + else: + for document in edi_document['order_line']: + if document.has_key(key): + del document[key] + return super(sale_order,self).edi_import(cr, uid, edi_document, context=context) + +sale_order() + +class sale_order_line(osv.osv, ir_edi.edi): + _inherit='sale.order.line' + +sale_order_line() +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale/edi_sale_order_data.xml b/addons/sale/edi_sale_order_data.xml new file mode 100644 index 00000000000..a9e25bd2562 --- /dev/null +++ b/addons/sale/edi_sale_order_data.xml @@ -0,0 +1,76 @@ + + + + + + context.update({'edi_web_url_view': '%s/edi/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) +if not object.partner_id.opt_out: self.pool.get('email.template').generate_mail(cr, + uid, + self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sale', 'email_template_edi_sale')[1], + [object.id], + context=context) + + code + ir.actions.server + + True + EDI Document - Sale Order + + + + + + + + + + + + ${object.company_id.name} - Sale Order ${object.name} + ${object.partner_invoice_id.email} + + True + +<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); "> + +<p> Hello ${object.partner_invoice_id.name and ' ' or ''},</p> +<p> You can click on the following link to preview, print and pay invoice: <br/> + <a href="${object._context.get('edi_web_url_view')}">${object._context.get('edi_web_url_view')} </a> +</p> + + +<p style="border-left: 1px solid #8e0000; margin-left: 30px;"> <strong>REFERENCES</strong><br /> Order number: <strong>${object.name}</strong><br /> Order amount: <strong>${object.amount_total} </strong><br /> Confirm date: ${object.date_confirm or 'n/a'}<br /> Your contact: <a href="mailto:${object.user_id.user_email or ''}?subject=Order%20${object.name}">${object.user_id.name}</a></p> + +${object.company_id.paypal_account and "<p>It is possible to pay with Paypal: <br/> <a href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Invoice%%20%s&invoice=%s&amount=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s\"><img src=\"https://www.paypalobjects.com/en_US/i/btn/btn_paynowCC_LG.gif\" style=\"margin-left: 100px; border: 0px; padding: 1px; text-decoration: none;\"/></a> </p>"%(object.company_id.paypal_account, object.name and object.name.replace('/','%2f') or '', object.name and object.name.replace('/','%2f') or '', object.amount_total) or ''} + +<p> If you have any question, do not hesitate to reply directly to this e-mail.</p> <p> Thank you for choosing OpenERP!<br /> </p> <div style="width: 375px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; overflow-x: hidden; overflow-y: hidden; zoom: 1; background-image: url(http://www.openerp.com/sites/default/files/red_gradient_bg.png); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(142, 0, 0); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-bottom-right-radius: 0px 0px; border-bottom-left-radius: 0px 0px; background-position: 0% 0%; background-repeat: repeat no-repeat; "> <h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 9px; padding-right: 14px; padding-bottom: 9px; padding-left: 14px; font-size: 12px; font-weight: normal; font-style: normal; color: rgb(255, 255, 255); "> <strong>${object.company_id.name}</strong></h3> </div> <div style="width: 347px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 12px; padding-right: 14px; padding-bottom: 12px; padding-left: 14px; overflow-x: hidden; overflow-y: hidden; zoom: 1; line-height: 16px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(242, 242, 242); "> <div> Contact:<a href="mailto:${object.user_id.user_email or ''}?subject=Order%20${object.name}">${object.user_id.name}</a></div> <div> </div> </div> </div> <p> </p> + + +Hello ${object.partner_invoice_id.name and ' ' or ''}, + +You can click on the following link to preview, print and pay invoice: + + ${object._context.get('edi_web_url_view') or 'n/a'} + +Order Number: *${object.name}* +Amount: *${object.amount_total}* +Confirm date: ${object.date_confirm or 'n/a'} +Your contact: ${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''} + +${object.company_id.paypal_account and "It is possible to pay with Paypal: https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Invoice%%20%s&invoice=%s&amount=%s&currency_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s"%(object.company_id.paypal_account, object.name and object.name.replace('/','%2f') or '', object.name and object.name.replace('/','%2f') or '', object.amount_total) or ''} + +If you have any question, do not hesitate to reply directly to this e-mail. + +Thank you for choosing our service! + +-- +${object.company_id.name} +Contact: ${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''} + + mako + Mail Template of Sale Order For EDI Document + sale.order + ${object.user_id.user_email or ''} + + + diff --git a/addons/sale/test/edi_sale_order.yml b/addons/sale/test/edi_sale_order.yml new file mode 100644 index 00000000000..8ad461ea476 --- /dev/null +++ b/addons/sale/test/edi_sale_order.yml @@ -0,0 +1,51 @@ +- + I create a partner which is a my customer ====================================== +- + !record {model: res.partner, id: res_partner_test22}: + name: Junjun wala + supplier: False + customer: True + opt_out: False +- + I create one Sale Order ======================================= +- + !record {model: sale.order, id: sale_order_test}: + partner_id: res_partner_test22 + partner_invoice_id: base.res_partner_address_3 + partner_order_id: base.res_partner_address_3 + partner_shipping_id: base.res_partner_address_3 + pricelist_id: 1 + order_line: + - product_id: product.product_product_pc1 + product_uom_qty: 1.0 + product_uom: 1 + price_unit: 150.0 + name: 'basic pc' +- + I Open the sale order ============================ +- + !python {model: sale.order}: | + + orders = self.browse(cr, uid, ref("sale_order_test")) + import netsvc + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'sale.order',orders.id,'order_confirm', cr) + +- + I Tesing of EDI functionality. First I export Sale Order from my company than import that Order into customer company +- + !python {model: ir.edi.document}: | + import json + invoice_pool = self.pool.get('sale.order') + orders = invoice_pool.browse(cr, uid, ref("sale_order_test")) + + tokens = self.export_edi(cr, uid, [orders]) + assert tokens, 'Token is not generated' + document = self.get_document(cr, uid, tokens[0]) + document = json.loads(document) + document[0]["__model"] = "purchase.order" + document = json.dumps(document) + a = self.import_edi(cr, uid, edi_document = document) + assert a, 'Invoice is not imported' + + From 5236ef43f183ae40f53bb432b184be81a151b707 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Thu, 8 Sep 2011 15:13:29 +0530 Subject: [PATCH 032/132] [IMP] accout.edi_invoice: to split edi_import() in smaller methods for importing the different parts bzr revid: hmo@tinyerp.com-20110908094329-14cutn414nbpvgvr --- addons/account/edi_invoice.py | 147 +++++++++++++++++++--------------- 1 file changed, 84 insertions(+), 63 deletions(-) diff --git a/addons/account/edi_invoice.py b/addons/account/edi_invoice.py index 179f2eb7234..cbcddb2430d 100644 --- a/addons/account/edi_invoice.py +++ b/addons/account/edi_invoice.py @@ -113,50 +113,57 @@ class account_invoice(osv.osv, ir_edi.edi): edi_doc_list.append(edi_doc) return edi_doc_list - 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. - """ - - partner_pool = self.pool.get('res.partner') - partner_address_pool = self.pool.get('res.partner.address') - model_data_pool = self.pool.get('ir.model.data') - product_pool = self.pool.get('product.product') - product_categ_pool = self.pool.get('product.category') - company_pool = self.pool.get('res.company') - country_pool = self.pool.get('res.country') - state_pool = self.pool.get('res.country.state') - account_journal_pool = self.pool.get('account.journal') - invoice_line_pool = self.pool.get('account.invoice.line') - account_pool = self.pool.get('account.account') - tax_id = [] - account_id = [] - partner_id = None - company_id = None + def get_invoice_journal(self, cr, uid, invoice_type, context=None): if context is None: context = {} - + account_journal_pool = self.pool.get('account.journal') + journal_context = context.copy() + journal_context.update({'type':invoice_type}) + journal_id = self._get_journal(cr, uid, context=journal_context) + journal = False + if journal_id: + journal = account_journal_pool.browse(cr, uid, journal_id, context=context) + return journal + + def get_tax_account(self, cr, uid, invoice_type='out_invoice', context=None): + #TOCHECK: should select account of output VAT for Customer Invoice and Input VAT for Supplier Invoice + 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 get_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 get_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) + account = False + if invoice_type in ('out_invoice','out_refund'): + account = product.product_tmpl_id.property_account_income + if not account: + account = product.categ_id.property_account_income_categ + else: + account = product.product_tmpl_id.property_account_expense + if not account: + account = product.categ_id.property_account_expense_categ + return account + + def edi_import_company(self, cr, uid, edi_document, context=None): # 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 + + partner_pool = self.pool.get('res.partner') + partner_address_pool = self.pool.get('res.partner.address') edi_company_address = edi_document['company_address'] edi_partner_id = edi_document['partner_id'] company_name = edi_document['company_id'][1] @@ -198,15 +205,43 @@ class account_invoice(osv.osv, ir_edi.edi): partner_address = partner_address_pool.browse(cr, uid, address_id, context=context) 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 = {} + + #import company as a new partner + partner_id = self.edi_import_company(cr, uid, edi_document, 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 # Set Account - if invoice_type in ('out_invoice', 'out_refund'): - invoice_account = partner.property_account_receivable - else: - invoice_account = partner.property_account_payable + invoice_account = self.get_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' @@ -222,13 +257,9 @@ class account_invoice(osv.osv, ir_edi.edi): del edi_document['company_id'] # journal_id: should be selected based on type: simply put the 'type' in the context when calling create(), will be selected correctly - journal_context = context.copy() - journal_context.update({'type':invoice_type}) - journal_id = self._get_journal(cr, uid, context=journal_context) - journal = False - if journal_id: - journal = account_journal_pool.browse(cr, uid, journal_id, context=context) + journal = self.get_invoice_journal(cr, uid, invoice_type, context=context) edi_document['journal_id'] = journal and self.edi_m2o(cr, uid, journal, context=context) or False + # 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.get('invoice_line', []): product_id = edi_invoice_line.get('product_id', False) @@ -236,16 +267,7 @@ class account_invoice(osv.osv, ir_edi.edi): if product_id: product_name = product_id and product_id[1] product_id = self.edi_import_relation(cr, uid, 'product.product', product_name, context=context) - product = product_pool.browse(cr, uid, product_id, context=context) - - if invoice_type in ('out_invoice','out_refund'): - account = product.product_tmpl_id.property_account_income - if not account: - account = product.categ_id.property_account_income_categ - else: - account = product.product_tmpl_id.property_account_expense - if not account: - account = product.categ_id.property_account_expense_categ + account = self.get_product_account(cr, uid, product_id, invoice_type, context=context) # TODO: add effect of fiscal position # account = fpos_obj.map_account(cr, uid, fiscal_position_id, account.id) edi_invoice_line['account_id'] = account and self.edi_m2o(cr, uid, account, context=context) or False @@ -253,10 +275,9 @@ class account_invoice(osv.osv, ir_edi.edi): # 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. for edi_tax_line in edi_document.get('tax_line', []): - account_ids = account_pool.search(cr, uid, [('type','<>','view'),('type','<>','income'), ('type', '<>', 'closed')]) - if account_ids: - tax_account = account_pool.browse(cr, uid, account_ids[0]) - edi_tax_line['account_id'] = self.edi_m2o(cr, uid, tax_account, context=context) #TODO should select account of output VAT for Customer Invoice and Input VAT for Supplier Invoice + tax_account = self.get_tax_account(cr, uid, context=context) + if tax_account: + edi_tax_line['account_id'] = self.edi_m2o(cr, uid, tax_account, context=context) edi_tax_line['manual'] = True # TODO :=> payment_term: if set, create a default one based on name... From 4dc536c8ce05b870e332acb01d8f9aab55184256 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Tue, 13 Sep 2011 18:29:23 +0530 Subject: [PATCH 033/132] [IMP] account: improve edi import of invoice to split in different stuff [IMP] sale: edi import process and correct EDI struct on exort [IMP] purchase: edi import process and correct EDI struct on export bzr revid: hmo@tinyerp.com-20110913125923-2gbhgd1vzyd2qrwy --- addons/account/edi_invoice.py | 101 ++------- addons/account/test/test_edi_invoice.yml | 62 +++++- addons/purchase/edi_purchase_order.py | 231 ++++++++------------ addons/purchase/edi_purchase_order_data.xml | 4 +- addons/purchase/test/edi_purchase_order.yml | 60 ++++- addons/sale/__openerp__.py | 3 +- addons/sale/edi_sale_order.py | 223 ++++++++----------- addons/sale/test/data_test.yml | 1 + addons/sale/test/edi_sale_order.yml | 55 ++++- 9 files changed, 362 insertions(+), 378 deletions(-) diff --git a/addons/account/edi_invoice.py b/addons/account/edi_invoice.py index cbcddb2430d..db09e7ab6aa 100644 --- a/addons/account/edi_invoice.py +++ b/addons/account/edi_invoice.py @@ -66,20 +66,9 @@ class account_invoice(osv.osv, ir_edi.edi): 'base_amount': True, 'tax_amount': True, }, + #'paid': True, } - partner_pool = self.pool.get('res.partner') - partner_address_pool = self.pool.get('res.partner.address') - company_address_dict = { - 'street': True, - 'street2': True, - 'zip': True, - 'city': True, - 'state_id': True, - 'country_id': True, - 'email': True, - 'phone': True, - - } + company_pool = self.pool.get('res.company') edi_doc_list = [] for invoice in records: # Get EDI doc based on struct. The result will also contain all metadata fields and attachments. @@ -89,26 +78,10 @@ class account_invoice(osv.osv, ir_edi.edi): edi_doc = edi_doc[0] # Add company info and address - res = partner_pool.address_get(cr, uid, [invoice.company_id.partner_id.id], ['contact', 'invoice']) - contact_addr_id = res['contact'] - invoice_addr_id = res['invoice'] - - address = partner_address_pool.browse(cr, uid, invoice_addr_id, context=context) - edi_company_address_dict = {} - for key, value in company_address_dict.items(): - if not value: - continue - address_rec = getattr(address, key) - if not address_rec: - continue - if key.endswith('_id'): - address_rec = self.edi_m2o(cr, uid, address_rec, context=context) - edi_company_address_dict[key] = address_rec - + edi_company_document = company_pool.edi_export_address(cr, uid, [invoice.company_id], context=context)[invoice.company_id.id] edi_doc.update({ - 'company_address': edi_company_address_dict, - #'company_logo': inv_comp.logo,#TODO - #'paid': inv_comp.paid, #TODO + 'company_address': edi_company_document['company_address'], + #'company_logo': edi_company_document['company_logo'],#TODO }) edi_doc_list.append(edi_doc) return edi_doc_list @@ -158,54 +131,30 @@ class account_invoice(osv.osv, ir_edi.edi): return account def edi_import_company(self, cr, uid, edi_document, context=None): - # 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 - - partner_pool = self.pool.get('res.partner') partner_address_pool = self.pool.get('res.partner.address') - edi_company_address = edi_document['company_address'] - edi_partner_id = edi_document['partner_id'] - company_name = edi_document['company_id'][1] + partner_pool = self.pool.get('res.partner') + company_pool = self.pool.get('res.company') + + # import company as a new partner, if type==in then supplier=1, else customer=1 + # company_address data used to add address to new partner invoice_type = edi_document['type'] - state_id = edi_company_address.get('state_id', False) - state_name = state_id and state_id[1] - country_id = edi_company_address.get('country_id', False) - country_name = country_id and country_id[1] - - country_id = country_name and self.edi_import_relation(cr, uid, 'res.country', country_name, context=context) or False - state_id = state_name and self.edi_import_relation(cr, uid, 'res.country.state', state_name, - values={'country_id': country_id, 'code': state_name}, context=context) or False - address_value = { - 'street': edi_company_address.get('street', False), - 'street2': edi_company_address.get('street2', False), - 'zip': edi_company_address.get('zip', False), - 'city': edi_company_address.get('city', False), - 'state_id': state_id, - 'country_id': country_id, - 'email': edi_company_address.get('email', False), - 'phone': edi_company_address.get('phone', False), - - } - - - partner_value = {'name': company_name} + partner_value = {} if invoice_type in ('out_invoice', 'in_refund'): - partner_value.update({'customer': True, 'supplier': False}) + partner_value.update({'customer': True}) if invoice_type in ('in_invoice', 'out_refund'): - partner_value.update({'customer': False, 'supplier': True}) - - partner_id = partner_pool.create(cr, uid, partner_value, context=context) - address_value.update({'partner_id': partner_id}) - address_id = partner_address_pool.create(cr, uid, address_value, context=context) + partner_value.update({'supplier': True}) + partner_id = company_pool.edi_import_as_partner(cr, uid, edi_document, values=partner_value, context=context) + # partner_id field is modified to point to the new partner + res = partner_pool.address_get(cr, uid, [partner_id], ['contact', 'invoice']) + address_id = res['invoice'] partner = partner_pool.browse(cr, uid, partner_id, context=context) - edi_document['partner_id'] = self.edi_m2o(cr, uid, partner, context=context) - partner_address = partner_address_pool.browse(cr, uid, address_id, context=context) + edi_document['partner_id'] = self.edi_m2o(cr, uid, partner, context=context) edi_document['address_invoice_id'] = self.edi_m2o(cr, uid, partner_address, context=context) - - return partner.id + del edi_document['company_id'] + 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 @@ -251,10 +200,7 @@ class account_invoice(osv.osv, ir_edi.edi): # internal number: reset to False, auto-generated edi_document['internal_number'] = False - - # company should set by default so delete company data from edi Document - del edi_document['company_address'] - del edi_document['company_id'] + # journal_id: should be selected based on type: simply put the 'type' in the context when calling create(), will be selected correctly journal = self.get_invoice_journal(cr, uid, invoice_type, context=context) @@ -281,7 +227,6 @@ class account_invoice(osv.osv, ir_edi.edi): edi_tax_line['manual'] = True # TODO :=> payment_term: if set, create a default one based on name... - return super(account_invoice,self).edi_import(cr, uid, edi_document, context=context) account_invoice() @@ -294,3 +239,5 @@ class account_invoice_tax(osv.osv, ir_edi.edi): _inherit = "account.invoice.tax" account_invoice_tax() + + diff --git a/addons/account/test/test_edi_invoice.yml b/addons/account/test/test_edi_invoice.yml index 0a03c3ca9dc..e01e6aaa712 100644 --- a/addons/account/test/test_edi_invoice.yml +++ b/addons/account/test/test_edi_invoice.yml @@ -56,7 +56,7 @@ wf_service.trg_validate(uid, 'account.invoice',invoices.id,'invoice_open', cr) - - I Tesing of EDI functionality. First I export customer invoice from my company than import that invoice into customer company + I Testing of EDI functionality. First I export customer invoice from my company than import that invoice into customer company - !python {model: ir.edi.document}: | invoice_pool = self.pool.get('account.invoice') @@ -64,9 +64,60 @@ tokens = self.export_edi(cr, uid, [invoice]) assert tokens, 'Token is not generated' - document = self.get_document(cr, uid, tokens[0]) - a = self.import_edi(cr, uid, edi_document = document) - assert a, 'Invoice is not imported' +- + I import of EDI document of custmer invoice +- + !python {model: ir.edi.document}: | + invoice_pool = self.pool.get('account.invoice') + edi_document = { + "internal_number": "SAJ/2011/002", + "company_address": { + "city": "Gerompont", + "zip": "1367", + "__last_update": False, + "country_id": ["b22acf7a-ddcd-11e0-a4db-701a04e25543:base.be", "Belgium"], + "__id": "b22acf7a-ddcd-11e0-a4db-701a04e25543:base.main_address", + "phone": "(+32).81.81.37.00", + "street": "Chaussee de Namur 40" + }, + "company_id": ["b22acf7a-ddcd-11e0-a4db-701a04e25543:account.res_company_test11", "Thomson pvt. ltd."], + "currency_id": ["b22acf7a-ddcd-11e0-a4db-701a04e25543:base.EUR", "EUR (\u20ac)"], + "address_invoice_id": ["b22acf7a-ddcd-11e0-a4db-701a04e25543:base.res_partner_address_11", "Sebastien LANGE, France, Alencon, 1 place de l'\u00c9glise"], + "partner_id": ["b22acf7a-ddcd-11e0-a4db-701a04e25543:account.res_partner_test20", "Junjun wala"], + "__attachments": [], + "__module": "account", + "amount_total": 1010.0, + "date_invoice": "2011-06-22", + "amount_untaxed": 10.0, + "name": "selling product", + "__model": "account.invoice", + "__last_update": False, + "tax_line": [{ + "amount": 1000.0, + "manual": True, + "__id": "b22acf7a-ddcd-11e0-a4db-701a04e25543:b22acf7a-ddcd-11e0-a4db-701a04e25543:account.account_invoice_tax-4g4EutbiEMVl", + "name": "sale tax", + "__last_update": False + }], + "__id": "b22acf7a-ddcd-11e0-a4db-701a04e25543:account.customer_invoice_test", + "amount_tax": 1000.0, + "__version": [6, 1], + "type": "out_invoice", + "invoice_line": [{ + "uos_id": ["b22acf7a-ddcd-11e0-a4db-701a04e25543:product.product_uom_unit", "PCE"], + "name": "basic pc", + "__last_update": False, + "price_unit": 10.0, + "price_subtotal": 10.0, + "__id": "b22acf7a-ddcd-11e0-a4db-701a04e25543:b22acf7a-ddcd-11e0-a4db-701a04e25543:account.account_invoice_line-1RP3so-u2vV4", + "product_id": ["b22acf7a-ddcd-11e0-a4db-701a04e25543:product.product_product_pc1", "[PC1] Basic PC"], + "quantity": 1.0 + }] + } + + invoice_id = invoice_pool.edi_import(cr, uid, edi_document, context=context) + invoice_new = invoice_pool.browse(cr, uid, invoice_id, context=context) + assert invoice_id, 'Invoice is not imported' - I Checking the out invoice become in invoice or not after import - @@ -75,8 +126,7 @@ invoice_old = self.browse(cr, uid, ref("customer_invoice_test")) new_partner_id = self.pool.get('res.partner').name_search(cr, uid, invoice_old.company_id.name) assert new_partner_id, 'Partner is not created of Supplier' - - ids = self.search(cr, uid, [('partner_id','=',new_partner_id[0][0]),('reference','=',invoice_old.internal_number)]) + ids = self.search(cr, uid, [('partner_id','=',new_partner_id[0][0]),('reference','=',"SAJ/2011/002")]) assert ids, 'Invoice does not have created of party' invoice_new = self.browse(cr, uid, ids[0]) assert invoice_new.reference == invoice_old.internal_number, "internal number is not stored in reference" diff --git a/addons/purchase/edi_purchase_order.py b/addons/purchase/edi_purchase_order.py index 605e29621b1..cb19f57c103 100644 --- a/addons/purchase/edi_purchase_order.py +++ b/addons/purchase/edi_purchase_order.py @@ -22,58 +22,48 @@ from osv import fields, osv, orm from base.ir import ir_edi from tools.translate import _ -from datetime import date +from datetime import date, datetime, timedelta class purchase_order(osv.osv, ir_edi.edi): _inherit = 'purchase.order' def edi_export(self, cr, uid, records, edi_struct=None, context=None): - """Exports a supplier or customer invoice""" + """Exports a purchase order""" edi_struct = { + 'company_id': True, # -> to be changed into partner + 'name': True, + + 'date_order': True, + 'partner_id': True, + 'partner_address_id': True, #only one address needed + #SO: 'partner_order_id' + #PO: 'partner_address_id' + + 'pricelist_id': True, + 'notes': True, + #SO: 'note' + #PO: 'notes' + + 'amount_total': True, + 'amount_tax': True, + 'amount_untaxed': True, + 'order_line': { 'name': True, - 'origin': True, - 'date_order': True, - 'date_approve': True, - 'partner_id': True, - 'partner_address_id': True, - 'dest_address_id': True, - 'warehouse_id': True, - 'location_id': True, - 'pricelist_id': True, - 'validator' : True, - 'amount_tax': True, - 'amount_total': True, - 'amount_untaxed': True, - 'order_line': { - 'name': True, - 'product_qty': True, - 'date_planned': True, - 'taxes_id': True, - 'product_uom': True, - 'product_id': True, - 'move_dest_id': True, - 'price_unit': True, - 'order_id': True, - 'invoiced': True, - 'price_subtotal': True, - }, - 'invoice_ids': True, - 'shipped': True, - 'company_id': True, - } - partner_pool = self.pool.get('res.partner') - partner_address_pool = self.pool.get('res.partner.address') - company_address_dict = { - 'street': True, - 'street2': True, - 'zip': True, - 'city': True, - 'state_id': True, - 'country_id': True, - 'email': True, - 'phone': True, - + 'date_planned': True, + #SO: 'delay' : 'date_approve' - 'date_planned' + #PO: 'date_planned': 'date_approve' + 'delay' + + 'product_id': True, + 'product_uom': True, + 'price_unit': True, + 'product_qty': True, + #SO: 'product_uom_qty' + #PO: 'product_qty' + + 'notes': True, + } } + company_pool = self.pool.get('res.company') edi_doc_list = [] for order in records: # Get EDI doc based on struct. The result will also contain all metadata fields and attachments. @@ -83,107 +73,80 @@ class purchase_order(osv.osv, ir_edi.edi): edi_doc = edi_doc[0] # Add company info and address - res = partner_pool.address_get(cr, uid, [order.company_id.partner_id.id], ['contact', 'order']) - contact_addr_id = res['contact'] - invoice_addr_id = res['order'] - - address = partner_address_pool.browse(cr, uid, invoice_addr_id, context=context) - edi_company_address_dict = {} - for key, value in company_address_dict.items(): - if not value: - continue - address_rec = getattr(address, key) - if not address_rec: - continue - if key.endswith('_id'): - address_rec = self.edi_m2o(cr, uid, address_rec, context=context) - edi_company_address_dict[key] = address_rec - + edi_company_document = company_pool.edi_export_address(cr, uid, [order.company_id], context=context)[order.company_id.id] edi_doc.update({ - 'company_address': edi_company_address_dict, - #'company_logo': inv_comp.logo,#TODO - #'paid': inv_comp.paid, #TODO + 'company_address': edi_company_document['company_address'], + #'company_logo': edi_company_document['company_logo'],#TODO }) - edi_doc['__model'] = 'sale.order' edi_doc_list.append(edi_doc) - return edi_doc_list - def edi_import(self, cr, uid, edi_document, context=None): - - partner_pool = self.pool.get('res.partner') + def edi_import_company(self, cr, uid, edi_document, context=None): partner_address_pool = self.pool.get('res.partner.address') - model_data_pool = self.pool.get('ir.model.data') - product_pool = self.pool.get('product.product') - product_categ_pool = self.pool.get('product.category') + partner_pool = self.pool.get('res.partner') company_pool = self.pool.get('res.company') - country_pool = self.pool.get('res.country') - state_pool = self.pool.get('res.country.state') - account_journal_pool = self.pool.get('account.journal') - invoice_line_pool = self.pool.get('account.invoice.line') - account_pool = self.pool.get('account.account') - stock = self.pool.get('stock.location') - tax_id = [] - account_id = [] - partner_id = None - company_id = None + + # import company as a new partner, supplier=1. + # company_address data used to add address to new partner + partner_value = {'customer': True} + partner_id = company_pool.edi_import_as_partner(cr, uid, edi_document, values=partner_value, context=context) + + + # partner_id field is modified to point to the new partner + res = partner_pool.address_get(cr, uid, [partner_id], ['contact', 'invoice']) + address_id = res['invoice'] + partner = partner_pool.browse(cr, uid, partner_id, context=context) + partner_address = partner_address_pool.browse(cr, uid, address_id, context=context) + edi_document['partner_id'] = self.edi_m2o(cr, uid, partner, context=context) + edi_document['partner_address_id'] = self.edi_m2o(cr, uid, partner_address, context=context) + del edi_document['company_id'] + return partner_id + + def edi_get_pricelist(self, cr, uid, partner_id, context=None): + # return value = ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.list0", "Public Pricelist (EUR)"] + partner_model = self.pool.get('res.partner') + partner = partner_model.browse(cr, uid, partner_id, context=context) + pricelist = partner.property_product_pricelist_purchase + if not pricelist: + pricelist = self.pool.get('ir.model.data').get_object(cr, uid, 'purchase', 'list0', context=context) + return self.edi_m2o(cr, uid, pricelist, context=context) + + def edi_get_location(self, cr, uid, partner_id, context=None): + # return value = ["724f93ec-ddd0-11e0-88ec-701a04e25543:stock.stock_location_stock", "Stock"] + partner_model = self.pool.get('res.partner') + partner = partner_model.browse(cr, uid, partner_id, context=context) + location = partner.property_stock_customer + if not location: + location = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'stock_location_stock', context=context) + return self.edi_m2o(cr, uid, location, context=context) + + def edi_import(self, cr, uid, edi_document, context=None): if context is None: context = {} - print edi_document - # 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 - edi_company_address = edi_document['company_address'] - edi_partner_id = edi_document['partner_id'] - company_name = edi_document['shop_id'][1] - state_id = edi_company_address.get('state_id', False) - state_name = state_id and state_id[1] - country_id = edi_company_address.get('country_id', False) - country_name = country_id and country_id[1] - - country_id = country_name and self.edi_import_relation(cr, uid, 'res.country', country_name, context=context) or False - state_id = state_name and self.edi_import_relation(cr, uid, 'res.country.state', state_name, - values={'country_id': country_id, 'code': state_name}, context=context) or False - address_value = { - 'street': edi_company_address.get('street', False), - 'street2': edi_company_address.get('street2', False), - 'zip': edi_company_address.get('zip', False), - 'city': edi_company_address.get('city', False), - 'state_id': state_id, - 'country_id': country_id, - 'email': edi_company_address.get('email', False), - 'phone': edi_company_address.get('phone', False), - - } - partner_value = {'name': company_name} - partner_value.update({'customer': False, 'supplier': True}) - partner_id = partner_pool.create(cr, uid, partner_value, context=context) - address_value.update({'partner_id': partner_id}) - address_id = partner_address_pool.create(cr, uid, address_value, context=context) - partner_address = partner_address_pool.browse(cr, uid, address_id, context=context) - edi_document.update({'partner_address_id':self.edi_m2o(cr, uid, partner_address, context=context)}) - partner = partner_pool.browse(cr, uid, partner_id, context=context) - edi_document['partner_id'] = self.edi_m2o(cr, uid, partner, context=context) - location_id = stock.search(cr, uid,[('name','=','Stock')]) - location = stock.browse(cr, uid, location_id[0]) - edi_document.update({'dest_address_id': edi_document['partner_shipping_id'],'location_id': self.edi_m2o(cr, uid, location, context=context)}) - for line in range(len(edi_document['order_line'])): - product_qty = edi_document['order_line'][line]['product_uom_qty'] - edi_document['order_line'][line].update({'product_qty': product_qty,'taxes_id':edi_document['order_line'][line]['tax_id']}) - - # all fields are converted for purchase order import so unnecessary fields are deleted - delete_key = ['sequence','procurement_id','product_uom_qty','company_address','shop_id','create_date','picking_policy','order_policy','partner_order_id','partner_shipping_id','invoice_quantity','partner_invoice_id','price_subtotal','date_confirm'] - for key in delete_key: - if edi_document.has_key(key): - del edi_document[key] - else: - for document in edi_document['order_line']: - if document.has_key(key): - del document[key] - print "in the edi_purchase import",edi_document + model = edi_document['__model'] + assert model == 'sale.order', _('Could not import purchase order') + edi_document['__model'] = self._name + #import company as a new partner + partner_id = self.edi_import_company(cr, uid, edi_document, context=context) + + edi_document['partner_ref'] = edi_document['name'] + edi_document['notes'] = edi_document.get('note', False) + edi_document['pricelist_id'] = self.edi_get_pricelist(cr, uid, partner_id, context=context) + edi_document['location_id'] = self.edi_get_location(cr, uid, partner_id, context=context) + for order_line in edi_document['order_line']: + order_line['product_qty'] = order_line['product_uom_qty'] + date_order = datetime.strptime(edi_document['date_order'], "%Y-%m-%d") + delay = order_line.get('delay', 0.0) + order_line['date_planned'] = (date_order + timedelta(days=delay)).strftime("%Y-%m-%d") + # price_unit = price_unit - discount + discount = order_line.get('discount', 0.0) + price_unit = order_line['price_unit'] + if discount: + price_unit = price_unit * (1 - (discount or 0.0) / 100.0) + order_line['price_unit'] = price_unit + return super(purchase_order,self).edi_import(cr, uid, edi_document, context=context) - purchase_order() class purchase_order_line(osv.osv, ir_edi.edi): diff --git a/addons/purchase/edi_purchase_order_data.xml b/addons/purchase/edi_purchase_order_data.xml index 7351afe4a3f..d8a87fec0c9 100644 --- a/addons/purchase/edi_purchase_order_data.xml +++ b/addons/purchase/edi_purchase_order_data.xml @@ -19,9 +19,9 @@ if not object.partner_id.opt_out: self.pool.get('email.template').generate_mail( - + diff --git a/addons/purchase/test/edi_purchase_order.yml b/addons/purchase/test/edi_purchase_order.yml index 1fbc89db70a..ef3bbb4338b 100644 --- a/addons/purchase/test/edi_purchase_order.yml +++ b/addons/purchase/test/edi_purchase_order.yml @@ -5,7 +5,7 @@ name: jones white supplier: False customer: True - opt_out: False + opt_out: True - I create one Purchase Order - @@ -30,18 +30,56 @@ import netsvc wf_service = netsvc.LocalService("workflow") wf_service.trg_validate(uid, 'purchase.order',orders.id,'approved', cr) +- + I Tesing of EDI functionality. First I export Purchase Order from my company than import that Order into customer company - !python {model: ir.edi.document}: | order_pool = self.pool.get('purchase.order') - purchase_id = order_pool.search(cr, uid, ref("purchase_order_test")) - orders = order_pool.browse(cr, uid, purchase_id) - import json + order = order_pool.browse(cr, uid, ref("purchase_order_test")) - tokens = self.export_edi(cr, uid, [orders]) + tokens = self.export_edi(cr, uid, [order]) assert tokens, 'Token is not generated' - document = self.get_document(cr, uid, tokens[0]) - document = json.loads(document) - document[0]["__model"] = "sale.order" - document = json.dumps(document) - a = self.import_edi(cr, uid, edi_document = document) - + +- + I import purchase order from EDI Document of sale order +- + !python {model: ir.edi.document}: | + purchase_order_pool = self.pool.get('purchase.order') + edi_document = { + "order_line": [{ + "product_uom_qty": 1.0, + "name": "basic pc", + "product_uom": ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.product_uom_unit", "PCE"], + "sequence": 10, + "price_unit": 150.0, + "__last_update": False, + "__id": "724f93ec-ddd0-11e0-88ec-701a04e25543:724f93ec-ddd0-11e0-88ec-701a04e25543:sale.sale_order_line-LXEqeuI-SSP0", + "product_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.product_product_pc1", "[PC1] Basic PC"] + }], + "order_policy": "manual", + "company_address": { + "city": "Gerompont", + "zip": "1367", + "__last_update": False, + "country_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:base.be", "Belgium"], + "__id": "724f93ec-ddd0-11e0-88ec-701a04e25543:base.main_address", + "phone": "(+32).81.81.37.00", + "street": "Chaussee de Namur 40" + }, + "partner_order_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:base.res_partner_address_3", "Thomas Passot, Belgium, Louvain-la-Neuve, Rue de l'Angelique, 1"], + "__id": "724f93ec-ddd0-11e0-88ec-701a04e25543:sale.sale_order_test", + "date_order": "2011-09-13", + "partner_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:sale.res_partner_test22", "Junjun wala"], + "__attachments": [], + "__module": "sale", + "amount_total": 150.0, + "amount_untaxed": 150.0, + "name": "SO008", + "__model": "sale.order", + "__last_update": False, + "company_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:base.main_company", "OpenERP S.A."], + "__version": [6, 1], + "pricelist_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.list0", "Public Pricelist (EUR)"] + } + new_purchase_order_id = purchase_order_pool.edi_import(cr, uid, edi_document, context=context) + assert new_purchase_order_id, 'Purchase order is not imported' diff --git a/addons/sale/__openerp__.py b/addons/sale/__openerp__.py index 0e3ec9ed075..c862a4cbbfa 100644 --- a/addons/sale/__openerp__.py +++ b/addons/sale/__openerp__.py @@ -89,6 +89,7 @@ Dashboard for Sales Manager that includes: ], 'demo_xml': ['sale_demo.xml'], 'test': [ + 'test/edi_sale_order.yml', 'test/data_test.yml', 'test/manual_order_policy.yml', 'test/prepaid_order_policy.yml', @@ -100,7 +101,7 @@ Dashboard for Sales Manager that includes: 'test/invoice_on_ordered_qty.yml', 'test/invoice_on_shipped_qty.yml', 'test/sale_report.yml', - 'test/edi_sale_order.yml', + ], 'installable': True, 'active': False, diff --git a/addons/sale/edi_sale_order.py b/addons/sale/edi_sale_order.py index 7e193fa672c..70a57a8d216 100644 --- a/addons/sale/edi_sale_order.py +++ b/addons/sale/edi_sale_order.py @@ -22,7 +22,7 @@ from osv import fields, osv, orm from base.ir import ir_edi from tools.translate import _ -from datetime import date +from datetime import date, datetime class sale_order(osv.osv, ir_edi.edi): _inherit = 'sale.order' @@ -30,61 +30,44 @@ class sale_order(osv.osv, ir_edi.edi): def edi_export(self, cr, uid, records, edi_struct=None, context=None): """Exports a Sale order""" edi_struct = { + 'company_id': True, # -> to be changed into partner + 'name': True, + 'date_order': True, + 'partner_id': True, + 'partner_order_id': True, #only one address needed + #SO: 'partner_order_id' + #PO: 'partner_address_id' + 'pricelist_id': True, + 'note': True, + #SO: 'note' + #PO: 'notes' + 'amount_total': True, + 'amount_tax': True, + 'amount_untaxed': True, + 'order_line': { + 'sequence': True, + #SO: yes + #PO: No 'name': True, - 'origin' : True, - 'picking_policy': True, - 'order_policy': True, - 'client_order_ref': True, - 'date_order': True, - 'partner_id': True, - 'note': True, - 'amount_untaxed': True, - 'payment_term': True, - 'amount_tax': True, - 'amount_total': True, - 'amount_untaxed': True, - 'pricelist_id': True, - 'incoterm': True, - 'partner_order_id': True, - 'partner_invoice_id': True, - 'partner_shipping_id': True, - 'shipped': True, - 'picked_rate': True, - 'invoice_quantity': True, - 'invoiced': True, - 'invoiced_rate': True, - - 'order_line': { - 'product_uos_qty': True, - 'product_uom': True, - 'sequence': True, - 'price_unit': True, - 'product_uom_qty': True, - 'discount': True, - 'product_uos': True, - 'invoiced': True, - 'delay': True, - 'name': True, - 'notes': True, - 'product_id': True, - 'th_weight': True, - 'product_packaging': True, - 'tax_id': True, - }, - } - partner_pool = self.pool.get('res.partner') - partner_address_pool = self.pool.get('res.partner.address') - company_address_dict = { - 'street': True, - 'street2': True, - 'zip': True, - 'city': True, - 'state_id': True, - 'country_id': True, - 'email': True, - 'phone': True, - + 'delay': True, + #SO: 'delay' : 'date_approve' - 'date_planned' + #PO: 'date_planned': 'date_approve' + 'delay' + + 'product_id': True, + 'product_uom': True, + 'price_unit': True, + 'product_uom_qty': True, + #SO: 'product_uom_qty' + #PO: 'product_qty' + + 'discount': True, + #SO: yes + #PO: No + 'notes': True, + } + } + company_pool = self.pool.get('res.company') edi_doc_list = [] for order in records: # Get EDI doc based on struct. The result will also contain all metadata fields and attachments. @@ -94,101 +77,67 @@ class sale_order(osv.osv, ir_edi.edi): edi_doc = edi_doc[0] # Add company info and address - res = partner_pool.address_get(cr, uid, [order.shop_id.company_id.partner_id.id], ['contact', 'order']) - contact_addr_id = res['contact'] - invoice_addr_id = res['order'] - - address = partner_address_pool.browse(cr, uid, invoice_addr_id, context=context) - edi_company_address_dict = {} - for key, value in company_address_dict.items(): - if not value: - continue - address_rec = getattr(address, key) - if not address_rec: - continue - if key.endswith('_id'): - address_rec = self.edi_m2o(cr, uid, address_rec, context=context) - - edi_company_address_dict[key] = address_rec - + edi_company_document = company_pool.edi_export_address(cr, uid, [order.company_id], context=context)[order.company_id.id] edi_doc.update({ - 'company_address': edi_company_address_dict, + 'company_address': edi_company_document['company_address'], + #'company_logo': edi_company_document['company_logo'],#TODO }) edi_doc_list.append(edi_doc) return edi_doc_list - def edi_import(self, cr, uid, edi_document, context=None): - partner_pool = self.pool.get('res.partner') + def edi_import_company(self, cr, uid, edi_document, context=None): partner_address_pool = self.pool.get('res.partner.address') - model_data_pool = self.pool.get('ir.model.data') - product_pool = self.pool.get('product.product') - product_categ_pool = self.pool.get('product.category') + partner_pool = self.pool.get('res.partner') company_pool = self.pool.get('res.company') - country_pool = self.pool.get('res.country') - state_pool = self.pool.get('res.country.state') - - tax_id = [] - account_id = [] - partner_id = None - company_id = None + + # import company as a new partner, supplier=1. + # company_address data used to add address to new partner + partner_value = {'supplier': True} + partner_id = company_pool.edi_import_as_partner(cr, uid, edi_document, values=partner_value, context=context) + + + # partner_id field is modified to point to the new partner + res = partner_pool.address_get(cr, uid, [partner_id], ['contact', 'invoice']) + address_id = res['invoice'] + partner = partner_pool.browse(cr, uid, partner_id, context=context) + partner_address = partner_address_pool.browse(cr, uid, address_id, context=context) + edi_document['partner_id'] = self.edi_m2o(cr, uid, partner, context=context) + edi_document['partner_order_id'] = self.edi_m2o(cr, uid, partner_address, context=context) + edi_document['partner_invoice_id'] = edi_document['partner_order_id'] + edi_document['partner_shipping_id'] = edi_document['partner_order_id'] + del edi_document['company_id'] + return partner_id + + def edi_get_pricelist(self, cr, uid, partner_id, context=None): + # value = ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.list0", "Public Pricelist (EUR)"] + partner_model = self.pool.get('res.partner') + partner = partner_model.browse(cr, uid, partner_id, context=context) + pricelist = partner.property_product_pricelist + if not pricelist: + pricelist = self.pool.get('ir.model.data').get_object(cr, uid, 'product', 'list0', context=context) + return self.edi_m2o(cr, uid, pricelist, context=context) + + def edi_import(self, cr, uid, edi_document, context=None): if context is None: context = {} - # 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 - edi_company_address = edi_document['company_address'] - edi_partner_id = edi_document['partner_id'] - company_name = edi_document['company_id'][1] - state_id = edi_company_address.get('state_id', False) - state_name = state_id and state_id[1] - country_id = edi_company_address.get('country_id', False) - country_name = country_id and country_id[1] + model = edi_document['__model'] + assert model == 'purchase.order', _('Could not import sale order') + edi_document['__model'] = self._name + #import company as a new partner + partner_id = self.edi_import_company(cr, uid, edi_document, context=context) - country_id = country_name and self.edi_import_relation(cr, uid, 'res.country', country_name, context=context) or False - state_id = state_name and self.edi_import_relation(cr, uid, 'res.country.state', state_name, - values={'country_id': country_id, 'code': state_name}, context=context) or False - address_value = { - 'street': edi_company_address.get('street', False), - 'street2': edi_company_address.get('street2', False), - 'zip': edi_company_address.get('zip', False), - 'city': edi_company_address.get('city', False), - 'state_id': state_id, - 'country_id': country_id, - 'email': edi_company_address.get('email', False), - 'phone': edi_company_address.get('phone', False), - - } - - partner_value = {'name': company_name} - partner_value.update({'customer': True, 'supplier': False}) - - partner_id = partner_pool.create(cr, uid, partner_value, context=context) - address_value.update({'partner_id': partner_id}) - address_id = partner_address_pool.create(cr, uid, address_value, context=context) - partner_address = partner_address_pool.browse(cr, uid, [address_id], context=context) - partner_address_id = self.edi_o2m(cr, uid, [partner_address], context=context) - partner = partner_pool.browse(cr, uid, partner_id, context=context) - edi_document['partner_id'] = self.edi_m2o(cr, uid, partner, context=context) - edi_document.update({ - 'partner_invoice_id': partner_address_id, - 'partner_order_id': partner_address_id, - 'partner_shipping_id': partner_address_id, - #'product_uom_qty': edi_document['order_line'][0]['product_qty'], - 'delay': 10, - }) - # all fields are converted for sale order import so unnecessary fields are deleted - del edi_document['order_line'][0]['date_planned'] - del edi_document['order_line'][0]['product_qty'] - del edi_document['date_approve'] - del edi_document['validator'] - - del edi_document['location_id'] - del edi_document['partner_address_id'] - del edi_document['company_address'] - del edi_document['company_id'] - del edi_document['warehouse_id'] + date_order = edi_document.get('date_order', False) + edi_document['client_order_ref'] = edi_document['name'] + edi_document['note'] = edi_document.get('notes', False) + edi_document['pricelist_id'] = self.edi_get_pricelist(cr, uid, partner_id, context=context) + order_lines = edi_document['order_line'] + for order_line in order_lines: + order_line['product_uom_qty'] = order_line['product_qty'] + date_order = datetime.strptime(date_order, "%Y-%m-%d") + date_planned = datetime.strptime(order_line['date_planned'], "%Y-%m-%d") + order_line['delay'] = (date_planned - date_order).days return super(sale_order,self).edi_import(cr, uid, edi_document, context=context) sale_order() diff --git a/addons/sale/test/data_test.yml b/addons/sale/test/data_test.yml index 6e5ec1a9738..2b75e27c90a 100644 --- a/addons/sale/test/data_test.yml +++ b/addons/sale/test/data_test.yml @@ -177,6 +177,7 @@ category_id: - res_partner_category_customers0 name: Cleartrail + opt_out: True - I create contact address for Cleartrail. - diff --git a/addons/sale/test/edi_sale_order.yml b/addons/sale/test/edi_sale_order.yml index b7bd018156e..9eb6fc42ce0 100644 --- a/addons/sale/test/edi_sale_order.yml +++ b/addons/sale/test/edi_sale_order.yml @@ -5,7 +5,7 @@ name: Junjun wala supplier: False customer: True - opt_out: False + opt_out: True - I create one Sale Order - @@ -35,17 +35,52 @@ I Tesing of EDI functionality. First I export Sale Order from my company than import that Order into customer company - !python {model: ir.edi.document}: | - import json - invoice_pool = self.pool.get('sale.order') - orders = invoice_pool.browse(cr, uid, ref("sale_order_test")) + sale_order_pool = self.pool.get('sale.order') + orders = sale_order_pool.browse(cr, uid, ref("sale_order_test")) tokens = self.export_edi(cr, uid, [orders]) assert tokens, 'Token is not generated' - document = self.get_document(cr, uid, tokens[0]) - document = json.loads(document) - document[0]["__model"] = "purchase.order" - document = json.dumps(document) - #a = self.import_edi(cr, uid, edi_document = document) - #assert a, 'Invoice is not imported' +- + I import Sale order from EDI Document of purchase order +- + !python {model: ir.edi.document}: | + sale_order_pool = self.pool.get('sale.order') + edi_document = { + "order_line": [{ + "name": "basic pc", + "product_uom": ["5af1272e-dd26-11e0-b65e-701a04e25543:product.product_uom_unit", "PCE"], + "date_planned": "2011-08-31", + "price_unit": 150.0, + "__last_update": False, + "__id": "5af1272e-dd26-11e0-b65e-701a04e25543:purchase.purchase_order_line-AlhsVDZGoKvJ", + "product_qty": 1.0, + "product_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:product.product_product_pc1", "[PC1] Basic PC"] + }], + "partner_address_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:base.res_partner_address_11", "Sebastien LANGE, France, Alencon, 1 place de l'\u00c9glise"], + "__id": "5af1272e-dd26-11e0-b65e-701a04e25543:purchase.purchase_order_test", + "date_order": "2011-09-12", + "partner_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:purchase.res_partner_test20", "jones white"], + "__attachments": [], + "__module": "purchase", + "amount_total": 150.0, + "company_address": { + "phone": "(+32).81.81.37.00", + "street": "Chaussee de Namur 40", + "city": "Gerompont", + "zip": "1367", + "__last_update": False, + "country_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:base.be", "Belgium"], + "__id": "5af1272e-dd26-11e0-b65e-701a04e25543:base.main_address", + }, + "amount_untaxed": 150.0, + "name": "PO00011", + "__model": "purchase.order", + "__last_update": False, + "company_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:base.main_company", "OpenERP S.A."], + "__version": [6, 1], + "pricelist_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:product.list0", "Public Pricelist (EUR)"] + } + new_sale_order_id = sale_order_pool.edi_import(cr, uid, edi_document, context=context) + assert new_sale_order_id, 'Sale order is not imported' From fa802828877780cb0f1cbab1a45b1009b5d643b2 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Thu, 15 Sep 2011 11:00:43 +0530 Subject: [PATCH 034/132] [FIX] edi: correct datas of email_template bzr revid: hmo@tinyerp.com-20110915053043-a6f2d7ecwfbzspu1 --- addons/account/edi_invoice_action_data.xml | 23 +++++++------- addons/account/test/test_edi_invoice.yml | 5 ++-- addons/email_template/email_template.py | 1 + addons/purchase/edi_purchase_order_data.xml | 33 ++++++++++----------- addons/sale/edi_sale_order_data.xml | 28 ++++++++--------- 5 files changed, 44 insertions(+), 46 deletions(-) diff --git a/addons/account/edi_invoice_action_data.xml b/addons/account/edi_invoice_action_data.xml index fb89e585195..22d3eb7dbdf 100644 --- a/addons/account/edi_invoice_action_data.xml +++ b/addons/account/edi_invoice_action_data.xml @@ -4,10 +4,10 @@ context.update({'edi_web_invoice_url_view': '%s/edi/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) -if not object.partner_id.opt_out: self.pool.get('email.template').generate_mail(cr, +if not object.partner_id.opt_out: self.pool.get('email.template').send_mail(cr, uid, self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'email_template_edi_invoice')[1], - [object.id], + object.id, context=context) code @@ -26,10 +26,14 @@ if not object.partner_id.opt_out: self.pool.get('email.template').generate_mail( - ${object.company_id.name} Invoice (Ref ${object.number or 'n/a' }) - ${object.address_invoice_id.email or ''} - - + Mail Template of Invoice For EDI Document + ${object.user_id.user_email or ''} + ${object.user_id.user_email or ''} + + ${object.company_id.name} Invoice (Ref ${object.number or 'n/a' }) + ${object.address_invoice_id.email or ''} + + <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 ''},</p> @@ -44,7 +48,7 @@ ${object.company_id.paypal_account and "<p>It is possible to pay with Payp <p> If you have any question, do not hesitate to reply directly to this e-mail.</p> <p> Thank you for choosing OpenERP!<br /> </p> <div style="width: 375px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; overflow-x: hidden; overflow-y: hidden; zoom: 1; background-image: url(http://www.openerp.com/sites/default/files/red_gradient_bg.png); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(142, 0, 0); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-bottom-right-radius: 0px 0px; border-bottom-left-radius: 0px 0px; background-position: 0% 0%; background-repeat: repeat no-repeat; "> <h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 9px; padding-right: 14px; padding-bottom: 9px; padding-left: 14px; font-size: 12px; font-weight: normal; font-style: normal; color: rgb(255, 255, 255); "> <strong>${object.company_id.name}</strong></h3> </div> <div style="width: 347px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 12px; padding-right: 14px; padding-bottom: 12px; padding-left: 14px; overflow-x: hidden; overflow-y: hidden; zoom: 1; line-height: 16px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(242, 242, 242); "> <div> Contact:<a href="mailto:${object.user_id.user_email or ''}?subject=Invoice%20${object.number}">${object.user_id.name}</a></div> <div> </div> </div> </div> <p> </p> - + Hello ${object.address_invoice_id.name and ' ' or ''}, You can click on the following link to preview, print and pay invoice: @@ -67,10 +71,7 @@ Thank you for choosing our service! ${object.company_id.name} Contact: ${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''} - mako - Mail Template of Invoice For EDI Document - account.invoice - ${object.user_id.user_email or ''} + diff --git a/addons/account/test/test_edi_invoice.yml b/addons/account/test/test_edi_invoice.yml index e01e6aaa712..d29fe96fc89 100644 --- a/addons/account/test/test_edi_invoice.yml +++ b/addons/account/test/test_edi_invoice.yml @@ -123,13 +123,12 @@ - !python {model: account.invoice}: | - invoice_old = self.browse(cr, uid, ref("customer_invoice_test")) - new_partner_id = self.pool.get('res.partner').name_search(cr, uid, invoice_old.company_id.name) + new_partner_id = self.pool.get('res.partner').name_search(cr, uid, "Thomson pvt. ltd.") assert new_partner_id, 'Partner is not created of Supplier' ids = self.search(cr, uid, [('partner_id','=',new_partner_id[0][0]),('reference','=',"SAJ/2011/002")]) assert ids, 'Invoice does not have created of party' invoice_new = self.browse(cr, uid, ids[0]) - assert invoice_new.reference == invoice_old.internal_number, "internal number is not stored in reference" + 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" diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index 3917b3f2ddb..1a8ba49594d 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -382,5 +382,6 @@ class email_template(osv.osv): if context.has_key('default_type'): del context['default_type'] attachment_ids.append(ir_attachment.create(cr, uid, attachment_data, context)) + return message_id # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/purchase/edi_purchase_order_data.xml b/addons/purchase/edi_purchase_order_data.xml index d8a87fec0c9..e99fe687b5d 100644 --- a/addons/purchase/edi_purchase_order_data.xml +++ b/addons/purchase/edi_purchase_order_data.xml @@ -3,11 +3,11 @@ - context.update({'edi_web_url_view': '%s/edi/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) -if not object.partner_id.opt_out: self.pool.get('email.template').generate_mail(cr, + context.update({'edi_web_purchase_url_view': '%s/edi/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) +if not object.partner_id.opt_out: self.pool.get('email.template').send_mail(cr, uid, self.pool.get('ir.model.data').get_object_reference(cr, uid, 'purchase', 'email_template_edi_purchase')[1], - [object.id], + object.id, context=context) code @@ -19,23 +19,25 @@ if not object.partner_id.opt_out: self.pool.get('email.template').generate_mail( - + - ${object.company_id.name} - Purchase Order ${object.name} - ${object.partner_address_id.email} - - True - + Mail Template of Purchase Order For EDI Document + ${object.validator.user_email or ''} + ${object.validator.user_email or ''} + ${object.company_id.name} - Purchase Order ${object.name} + ${object.partner_address_id.email} + + <div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); "> <p> Hello ${object.partner_address_id.name and ' ' or ''},</p> <p> You can click on the following link to preview, print and pay invoice: <br/> - <a href="${object._context.get('edi_web_url_view')}">${object._context.get('edi_web_url_view')} </a> + <a href="${object._context.get('edi_web_purchase_url_view')}">${object._context.get('edi_web_purchase_url_view')} </a> </p> @@ -45,12 +47,12 @@ ${object.company_id.paypal_account and "<p>It is possible to pay with Payp <p> If you have any question, do not hesitate to reply directly to this e-mail.</p> <p> Thank you for choosing OpenERP!<br /> </p> <div style="width: 375px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; overflow-x: hidden; overflow-y: hidden; zoom: 1; background-image: url(http://www.openerp.com/sites/default/files/red_gradient_bg.png); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(142, 0, 0); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-bottom-right-radius: 0px 0px; border-bottom-left-radius: 0px 0px; background-position: 0% 0%; background-repeat: repeat no-repeat; "> <h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 9px; padding-right: 14px; padding-bottom: 9px; padding-left: 14px; font-size: 12px; font-weight: normal; font-style: normal; color: rgb(255, 255, 255); "> <strong>${object.company_id.name}</strong></h3> </div> <div style="width: 347px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 12px; padding-right: 14px; padding-bottom: 12px; padding-left: 14px; overflow-x: hidden; overflow-y: hidden; zoom: 1; line-height: 16px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(242, 242, 242); "> <div> Contact:<a href="mailto:${object.validator.user_email or ''}?subject=Order%20${object.name}">${object.validator.name}</a></div> <div> </div> </div> </div> <p> </p> - + Hello ${object.partner_address_id.name and ' ' or ''}, You can click on the following link to preview, print and pay invoice: - ${object._context.get('edi_web_url_view') or 'n/a'} + ${object._context.get('edi_web_purchase_url_view') or 'n/a'} Order Number: *${object.name}* Amount: *${object.amount_total}* @@ -67,10 +69,7 @@ Thank you for choosing our service! ${object.company_id.name} Contact: ${object.validator.name} ${object.validator.user_email and '<%s>'%(object.validator.user_email) or ''} - mako - Mail Template of Purchase Order For EDI Document - purchase.order - ${object.validator.user_email or ''} + diff --git a/addons/sale/edi_sale_order_data.xml b/addons/sale/edi_sale_order_data.xml index a9e25bd2562..0e9fa04b12e 100644 --- a/addons/sale/edi_sale_order_data.xml +++ b/addons/sale/edi_sale_order_data.xml @@ -3,11 +3,11 @@ - context.update({'edi_web_url_view': '%s/edi/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) -if not object.partner_id.opt_out: self.pool.get('email.template').generate_mail(cr, + context.update({'edi_web_sale_url_view': '%s/edi/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) +if not object.partner_id.opt_out: self.pool.get('email.template').send_email(cr, uid, self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sale', 'email_template_edi_sale')[1], - [object.id], + object.id, context=context) code @@ -26,16 +26,18 @@ if not object.partner_id.opt_out: self.pool.get('email.template').generate_mail( - ${object.company_id.name} - Sale Order ${object.name} - ${object.partner_invoice_id.email} - - True - + Mail Template of Sale Order For EDI Document + ${object.user_id.user_email or ''} + ${object.user_id.user_email or ''} + ${object.company_id.name} - Sale Order ${object.name} + ${object.partner_invoice_id.email} + + <div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); "> <p> Hello ${object.partner_invoice_id.name and ' ' or ''},</p> <p> You can click on the following link to preview, print and pay invoice: <br/> - <a href="${object._context.get('edi_web_url_view')}">${object._context.get('edi_web_url_view')} </a> + <a href="${object._context.get('edi_web_sale_url_view')}">${object._context.get('edi_web_sale_url_view')} </a> </p> @@ -45,12 +47,12 @@ ${object.company_id.paypal_account and "<p>It is possible to pay with Payp <p> If you have any question, do not hesitate to reply directly to this e-mail.</p> <p> Thank you for choosing OpenERP!<br /> </p> <div style="width: 375px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; overflow-x: hidden; overflow-y: hidden; zoom: 1; background-image: url(http://www.openerp.com/sites/default/files/red_gradient_bg.png); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(142, 0, 0); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-bottom-right-radius: 0px 0px; border-bottom-left-radius: 0px 0px; background-position: 0% 0%; background-repeat: repeat no-repeat; "> <h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 9px; padding-right: 14px; padding-bottom: 9px; padding-left: 14px; font-size: 12px; font-weight: normal; font-style: normal; color: rgb(255, 255, 255); "> <strong>${object.company_id.name}</strong></h3> </div> <div style="width: 347px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 12px; padding-right: 14px; padding-bottom: 12px; padding-left: 14px; overflow-x: hidden; overflow-y: hidden; zoom: 1; line-height: 16px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(242, 242, 242); "> <div> Contact:<a href="mailto:${object.user_id.user_email or ''}?subject=Order%20${object.name}">${object.user_id.name}</a></div> <div> </div> </div> </div> <p> </p> - + Hello ${object.partner_invoice_id.name and ' ' or ''}, You can click on the following link to preview, print and pay invoice: - ${object._context.get('edi_web_url_view') or 'n/a'} + ${object._context.get('edi_web_sale_url_view') or 'n/a'} Order Number: *${object.name}* Amount: *${object.amount_total}* @@ -67,10 +69,6 @@ Thank you for choosing our service! ${object.company_id.name} Contact: ${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''} - mako - Mail Template of Sale Order For EDI Document - sale.order - ${object.user_id.user_email or ''} From cbc18d6479d37c13666056709fe1c2c82204a939 Mon Sep 17 00:00:00 2001 From: "Hardik Ansodariy (OpenERP)" Date: Thu, 15 Sep 2011 19:02:13 +0530 Subject: [PATCH 035/132] [IMP] improved yml for edi sale,purchase and account invoice bzr revid: han@tinyerp.com-20110915133213-i67qac35o1q1rtyv --- addons/account/test/test_edi_invoice.yml | 7 +++- addons/purchase/test/edi_purchase_order.yml | 39 ++++++++++++++++-- addons/sale/test/edi_sale_order.yml | 44 ++++++++++----------- 3 files changed, 61 insertions(+), 29 deletions(-) diff --git a/addons/account/test/test_edi_invoice.yml b/addons/account/test/test_edi_invoice.yml index 29c7c626f6d..843892a4573 100644 --- a/addons/account/test/test_edi_invoice.yml +++ b/addons/account/test/test_edi_invoice.yml @@ -150,11 +150,14 @@ 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', 'Imported in voice is not supplier invoice' + assert invoice_new.type == 'in_invoice', "Imported in voice is not supplier invoice" + assert invoice_new.amount_total == 1010.0, "Amount total is not same" + assert invoice_new.amount_tax == 1000.0, "Amount tax is not same" + assert invoice_new.amount_untaxes == 10.0, "Amount untaxed is not same" product = ['Medium PC','Basic PC'] for inv_line in invoice_new.invoice_line: assert inv_line.account_id.id, "account_id is not taken from product's default" - assert inv_line.product_id.name in product, "product is not in Invoice line" + assert inv_line.product_id.name in ['Medium PC','Basic PC'], "product is not in Invoice line" for inv_tax in invoice_new.tax_line: assert inv_tax.manual, "for tax line manual is not set to True" assert inv_tax.account_id, "for tax_line default accounts is not picked based on the tax config of the DB where it is imported." diff --git a/addons/purchase/test/edi_purchase_order.yml b/addons/purchase/test/edi_purchase_order.yml index ef3bbb4338b..a6dc45ee997 100644 --- a/addons/purchase/test/edi_purchase_order.yml +++ b/addons/purchase/test/edi_purchase_order.yml @@ -21,8 +21,15 @@ price_unit: 150.0 name: 'basic pc' date_planned: '2011-08-31' + order_line: + - product_id: product.product_product_pc3 + product_qty: 10.0 + product_uom: 1 + price_unit: 200.0 + name: 'medium pc' + date_planned: '2011-08-31' - - I Open the sale order + I Open the Purchase order - !python {model: purchase.order}: | @@ -55,6 +62,16 @@ "__last_update": False, "__id": "724f93ec-ddd0-11e0-88ec-701a04e25543:724f93ec-ddd0-11e0-88ec-701a04e25543:sale.sale_order_line-LXEqeuI-SSP0", "product_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.product_product_pc1", "[PC1] Basic PC"] + }, + { + "product_uom_qty": 10.0, + "name": "medium pc", + "product_uom": ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.product_uom_unit", "PCE"], + "sequence": 11, + "price_unit": 200.0, + "__last_update": False, + "__id": "724f93ec-ddd0-11e0-88ec-701a04e25543:724f93ec-ddd0-11e0-88ec-701a04e25543:sale.sale_order_line-LXEqeuI-SSP0", + "product_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.product_product_pc3", "[PC3] Medim PC"] }], "order_policy": "manual", "company_address": { @@ -72,8 +89,8 @@ "partner_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:sale.res_partner_test22", "Junjun wala"], "__attachments": [], "__module": "sale", - "amount_total": 150.0, - "amount_untaxed": 150.0, + "amount_total": 350.0, + "amount_untaxed": 350.0, "name": "SO008", "__model": "sale.order", "__last_update": False, @@ -83,3 +100,19 @@ } new_purchase_order_id = purchase_order_pool.edi_import(cr, uid, edi_document, context=context) assert new_purchase_order_id, 'Purchase order is not imported' + +- + I Checking the sale order become purchase order or not after import +- + !python {model: sale.order}: | + + ids = self.search(cr, uid, [('partner_id','=','OpenERP S.A.')]) + assert ids, 'Order does not have created of party' + order_new = self.browse(cr, uid, ids[0]) + assert order_new.pricelist_id.name == 'Public Pricelist' , "Public Price list is not imported" + assert order_new.amount_total == 350, "Amount total is not same" + assert order_new.amount_untaxed == 350, "untaxed amount is not same" + for sale_line in order_new.order_line: + assert sale_line.name in ['basic pc','medium pc'], "name of product is not in order" + assert sale_line.product_id.name in ['Basic PC','Medium PC'], "name of product is not in order" + assert sale_line.date_order == '2011-09-13' , "date of order is not same" diff --git a/addons/sale/test/edi_sale_order.yml b/addons/sale/test/edi_sale_order.yml index 1756f369ff6..af1b32a6f01 100644 --- a/addons/sale/test/edi_sale_order.yml +++ b/addons/sale/test/edi_sale_order.yml @@ -21,6 +21,12 @@ product_uom: 1 price_unit: 150.0 name: 'basic pc' + order_line: + - product_id: product.product_product_pc3 + product_uom_qty: 10.0 + product_uom: 1 + price_unit: 200.0 + name: 'medium pc' - I Open the sale order - @@ -63,7 +69,7 @@ "partner_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:purchase.res_partner_test20", "jones white"], "__attachments": [], "__module": "purchase", - "amount_total": 150.0, + "amount_total": 350.0, "company_address": { "phone": "(+32).81.81.37.00", "street": "Chaussee de Namur 40", @@ -73,7 +79,7 @@ "country_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:base.be", "Belgium"], "__id": "5af1272e-dd26-11e0-b65e-701a04e25543:base.main_address", }, - "amount_untaxed": 150.0, + "amount_untaxed": 350.0, "name": "PO00011", "__model": "purchase.order", "__last_update": False, @@ -85,27 +91,17 @@ assert new_sale_order_id, 'Sale order is not imported' - - I Checking the out invoice become in invoice or not after import ======== + I Checking the sale order become purchase order or not after import - - !python {model: account.invoice}: | + !python {model: purchase.order}: | - new_partner_id = self.pool.get('res.partner').name_search(cr, uid, "Thomson pvt. ltd.") - assert new_partner_id, 'Partner is not created of Supplier' - ids = self.search(cr, uid, [('partner_id','=',new_partner_id[0][0]),('reference','=',"SAJ/2011/002")]) - assert ids, 'Invoice does not have created of party' - invoice_new = self.browse(cr, uid, ids[0]) - 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', 'Imported in voice is not supplier invoice' - product = ['Medium PC','Basic PC'] - for inv_line in invoice_new.invoice_line: - assert inv_line.account_id.id, "account_id is not taken from product's default" - assert inv_line.product_id.name in product, "product is not in Invoice line" - #print '-----------------=================',inv_line.product_id.quantity - assert inv_line.product_id.price_subtotal == 375, 'subtotal is not matched' - #assert inv_line.producr_id.quantity - for inv_tax in invoice_new.tax_line: - assert inv_tax.manual, "for tax line manual is not set to True" - assert inv_tax.account_id, "for tax_line default accounts is not picked based on the tax config of the DB where it is imported." + ids = self.search(cr, uid, [('partner_id','=','OpenERP S.A.')]) + assert ids, 'Order does not have created of party' + order_new = self.browse(cr, uid, ids[0]) + assert order_new.pricelist_id.name == 'Public Pricelist' , "Public Price list is not imported" + assert order_new.amount_total == 350, "Amount total is not same" + assert order_new.amount_untaxed == 350, "untaxed amount is not same" + for purchase_line in order_new.order_line: + assert purchase_line.name in ['basic pc','medium pc'], "name of product is not in order" + assert purchase_line.product_id.name in ['Basic PC','Medium PC'], "name of product is not in order" + assert purchase_line.date_planned == '2011-09-13' , "date of planned order is not same" From a2df13b0a197934cde5f2f2a725bcba3135ce565 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Thu, 15 Sep 2011 19:13:01 +0530 Subject: [PATCH 036/132] [IMP] edi: modified web_url bzr revid: hmo@tinyerp.com-20110915134301-jbjssv0p43v6lczm --- addons/account/edi_invoice_action_data.xml | 2 +- addons/purchase/edi_purchase_order_data.xml | 2 +- addons/sale/edi_sale_order_data.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/account/edi_invoice_action_data.xml b/addons/account/edi_invoice_action_data.xml index 22d3eb7dbdf..517628b434c 100644 --- a/addons/account/edi_invoice_action_data.xml +++ b/addons/account/edi_invoice_action_data.xml @@ -3,7 +3,7 @@ - context.update({'edi_web_invoice_url_view': '%s/edi/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) + context.update({'edi_web_invoice_url_view': '%s/web/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) if not object.partner_id.opt_out: self.pool.get('email.template').send_mail(cr, uid, self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'email_template_edi_invoice')[1], diff --git a/addons/purchase/edi_purchase_order_data.xml b/addons/purchase/edi_purchase_order_data.xml index e99fe687b5d..7e6482b6ca1 100644 --- a/addons/purchase/edi_purchase_order_data.xml +++ b/addons/purchase/edi_purchase_order_data.xml @@ -3,7 +3,7 @@ - context.update({'edi_web_purchase_url_view': '%s/edi/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) + context.update({'edi_web_purchase_url_view': '%s/web/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) if not object.partner_id.opt_out: self.pool.get('email.template').send_mail(cr, uid, self.pool.get('ir.model.data').get_object_reference(cr, uid, 'purchase', 'email_template_edi_purchase')[1], diff --git a/addons/sale/edi_sale_order_data.xml b/addons/sale/edi_sale_order_data.xml index 0e9fa04b12e..66e38f40360 100644 --- a/addons/sale/edi_sale_order_data.xml +++ b/addons/sale/edi_sale_order_data.xml @@ -3,7 +3,7 @@ - context.update({'edi_web_sale_url_view': '%s/edi/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) + context.update({'edi_web_sale_url_view': '%s/web/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) if not object.partner_id.opt_out: self.pool.get('email.template').send_email(cr, uid, self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sale', 'email_template_edi_sale')[1], From c17142a45bc329931bb7ba59550fcdb4d7624b0c Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Fri, 16 Sep 2011 12:13:19 +0530 Subject: [PATCH 037/132] [FIX] edi: correct yml and remove stuff to change model into sale and puchase EDI bzr revid: hmo@tinyerp.com-20110916064319-63zc227t9g8cxao2 --- addons/account/test/test_edi_invoice.yml | 3 -- addons/purchase/edi_purchase_order.py | 3 -- addons/purchase/test/edi_purchase_order.yml | 20 +++++------ addons/sale/edi_sale_order.py | 11 +++--- addons/sale/test/edi_sale_order.yml | 40 +++++++++++++++------ 5 files changed, 45 insertions(+), 32 deletions(-) diff --git a/addons/account/test/test_edi_invoice.yml b/addons/account/test/test_edi_invoice.yml index 843892a4573..0285e541156 100644 --- a/addons/account/test/test_edi_invoice.yml +++ b/addons/account/test/test_edi_invoice.yml @@ -151,9 +151,6 @@ 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', "Imported in voice is not supplier invoice" - assert invoice_new.amount_total == 1010.0, "Amount total is not same" - assert invoice_new.amount_tax == 1000.0, "Amount tax is not same" - assert invoice_new.amount_untaxes == 10.0, "Amount untaxed is not same" product = ['Medium PC','Basic PC'] for inv_line in invoice_new.invoice_line: assert inv_line.account_id.id, "account_id is not taken from product's default" diff --git a/addons/purchase/edi_purchase_order.py b/addons/purchase/edi_purchase_order.py index cb19f57c103..8e3979eb8bd 100644 --- a/addons/purchase/edi_purchase_order.py +++ b/addons/purchase/edi_purchase_order.py @@ -124,9 +124,6 @@ class purchase_order(osv.osv, ir_edi.edi): if context is None: context = {} - model = edi_document['__model'] - assert model == 'sale.order', _('Could not import purchase order') - edi_document['__model'] = self._name #import company as a new partner partner_id = self.edi_import_company(cr, uid, edi_document, context=context) diff --git a/addons/purchase/test/edi_purchase_order.yml b/addons/purchase/test/edi_purchase_order.yml index a6dc45ee997..07f5179a65a 100644 --- a/addons/purchase/test/edi_purchase_order.yml +++ b/addons/purchase/test/edi_purchase_order.yml @@ -68,7 +68,7 @@ "name": "medium pc", "product_uom": ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.product_uom_unit", "PCE"], "sequence": 11, - "price_unit": 200.0, + "price_unit": 20.0, "__last_update": False, "__id": "724f93ec-ddd0-11e0-88ec-701a04e25543:724f93ec-ddd0-11e0-88ec-701a04e25543:sale.sale_order_line-LXEqeuI-SSP0", "product_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.product_product_pc3", "[PC3] Medim PC"] @@ -88,13 +88,13 @@ "date_order": "2011-09-13", "partner_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:sale.res_partner_test22", "Junjun wala"], "__attachments": [], - "__module": "sale", + "__module": "purchase", "amount_total": 350.0, "amount_untaxed": 350.0, "name": "SO008", - "__model": "sale.order", + "__model": "purchase.order", "__last_update": False, - "company_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:base.main_company", "OpenERP S.A."], + "company_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:base.main_company", "Supplier S.A."], "__version": [6, 1], "pricelist_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.list0", "Public Pricelist (EUR)"] } @@ -104,15 +104,15 @@ - I Checking the sale order become purchase order or not after import - - !python {model: sale.order}: | + !python {model: purchase.order}: | - ids = self.search(cr, uid, [('partner_id','=','OpenERP S.A.')]) + ids = self.search(cr, uid, [('partner_id','=','Supplier S.A.'), ('partner_ref', '=', 'SO008')]) assert ids, 'Order does not have created of party' order_new = self.browse(cr, uid, ids[0]) assert order_new.pricelist_id.name == 'Public Pricelist' , "Public Price list is not imported" assert order_new.amount_total == 350, "Amount total is not same" assert order_new.amount_untaxed == 350, "untaxed amount is not same" - for sale_line in order_new.order_line: - assert sale_line.name in ['basic pc','medium pc'], "name of product is not in order" - assert sale_line.product_id.name in ['Basic PC','Medium PC'], "name of product is not in order" - assert sale_line.date_order == '2011-09-13' , "date of order is not same" + for purchase_line in order_new.order_line: + assert purchase_line.name in ['basic pc','medium pc'], "name of product is not in order" + assert purchase_line.product_id.name in ['Basic PC','Medium PC'], "name of product is not in order" + assert purchase_line.date_order == '2011-09-13' , "date of order is not same" diff --git a/addons/sale/edi_sale_order.py b/addons/sale/edi_sale_order.py index 70a57a8d216..1baad0f59b5 100644 --- a/addons/sale/edi_sale_order.py +++ b/addons/sale/edi_sale_order.py @@ -122,9 +122,6 @@ class sale_order(osv.osv, ir_edi.edi): if context is None: context = {} - model = edi_document['__model'] - assert model == 'purchase.order', _('Could not import sale order') - edi_document['__model'] = self._name #import company as a new partner partner_id = self.edi_import_company(cr, uid, edi_document, context=context) @@ -135,9 +132,11 @@ class sale_order(osv.osv, ir_edi.edi): order_lines = edi_document['order_line'] for order_line in order_lines: order_line['product_uom_qty'] = order_line['product_qty'] - date_order = datetime.strptime(date_order, "%Y-%m-%d") - date_planned = datetime.strptime(order_line['date_planned'], "%Y-%m-%d") - order_line['delay'] = (date_planned - date_order).days + date_planned = order_line['date_planned'] + delay = 0 + if date_order and date_planned: + delay = (datetime.strptime(date_planned, "%Y-%m-%d") - datetime.strptime(date_order, "%Y-%m-%d")).days + order_line['delay'] = delay return super(sale_order,self).edi_import(cr, uid, edi_document, context=context) sale_order() diff --git a/addons/sale/test/edi_sale_order.yml b/addons/sale/test/edi_sale_order.yml index af1b32a6f01..cf8da5f1efb 100644 --- a/addons/sale/test/edi_sale_order.yml +++ b/addons/sale/test/edi_sale_order.yml @@ -56,19 +56,29 @@ "order_line": [{ "name": "basic pc", "product_uom": ["5af1272e-dd26-11e0-b65e-701a04e25543:product.product_uom_unit", "PCE"], - "date_planned": "2011-08-31", + "date_planned": "2011-09-30", "price_unit": 150.0, "__last_update": False, "__id": "5af1272e-dd26-11e0-b65e-701a04e25543:purchase.purchase_order_line-AlhsVDZGoKvJ", "product_qty": 1.0, "product_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:product.product_product_pc1", "[PC1] Basic PC"] + }, + { + "name": "medium pc", + "product_uom": ["5af1272e-dd26-11e0-b65e-701a04e25543:product.product_uom_unit", "PCE"], + "date_planned": "2011-09-15", + "price_unit": 100.0, + "__last_update": False, + "__id": "5af1272e-dd26-11e0-b65e-701a04e25543:purchase.purchase_order_line-AlhsVDZGoKvJ", + "product_qty": 2.0, + "product_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:product.product_product_pc3", "[PC3] Medium PC"] }], "partner_address_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:base.res_partner_address_11", "Sebastien LANGE, France, Alencon, 1 place de l'\u00c9glise"], "__id": "5af1272e-dd26-11e0-b65e-701a04e25543:purchase.purchase_order_test", "date_order": "2011-09-12", "partner_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:purchase.res_partner_test20", "jones white"], "__attachments": [], - "__module": "purchase", + "__module": "sale", "amount_total": 350.0, "company_address": { "phone": "(+32).81.81.37.00", @@ -81,9 +91,9 @@ }, "amount_untaxed": 350.0, "name": "PO00011", - "__model": "purchase.order", + "__model": "sale.order", "__last_update": False, - "company_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:base.main_company", "OpenERP S.A."], + "company_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:base.main_company", "Client S.A."], "__version": [6, 1], "pricelist_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:product.list0", "Public Pricelist (EUR)"] } @@ -93,15 +103,25 @@ - I Checking the sale order become purchase order or not after import - - !python {model: purchase.order}: | + !python {model: sale.order}: | - ids = self.search(cr, uid, [('partner_id','=','OpenERP S.A.')]) + ids = self.search(cr, uid, [('partner_id','=','Client S.A.'), ('client_order_ref', '=', 'PO00011')]) assert ids, 'Order does not have created of party' order_new = self.browse(cr, uid, ids[0]) assert order_new.pricelist_id.name == 'Public Pricelist' , "Public Price list is not imported" assert order_new.amount_total == 350, "Amount total is not same" assert order_new.amount_untaxed == 350, "untaxed amount is not same" - for purchase_line in order_new.order_line: - assert purchase_line.name in ['basic pc','medium pc'], "name of product is not in order" - assert purchase_line.product_id.name in ['Basic PC','Medium PC'], "name of product is not in order" - assert purchase_line.date_planned == '2011-09-13' , "date of planned order is not same" + assert len(order_new.order_line) == 2, "sale lines are not same" + for sale_line in order_new.order_line: + if sale_line.name == 'basic pc': + assert sale_line.delay == 18 , "delay is not same" + assert sale_line.product_uom.name == "PCE" , "uom is not same" + assert sale_line.price_unit == 150 , "price unit is not same" + assert sale_line.product_uom_qty == 1 , "product qty is not same" + elif sale_line.name == 'medium pc': + assert sale_line.delay == 3 , "delay is not same" + assert sale_line.product_uom.name == "PCE" , "uom is not same" + assert sale_line.price_unit == 100 , "price unit is not same" + assert sale_line.product_uom_qty == 2 , "product qty is not same" + else: + assert 'wrong product imported in sale lines' From 16fd6962a098ad8f1a5e80fa769267281377e94f Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Fri, 16 Sep 2011 15:06:11 +0530 Subject: [PATCH 038/132] [FIX] edi: typo and add some fields in export dict bzr revid: hmo@tinyerp.com-20110916093611-p5ee9b7for1gqx6a --- addons/purchase/edi_purchase_order.py | 8 +++++--- addons/sale/edi_sale_order.py | 8 +++++--- addons/sale/edi_sale_order_data.xml | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/addons/purchase/edi_purchase_order.py b/addons/purchase/edi_purchase_order.py index 8e3979eb8bd..e2791164c31 100644 --- a/addons/purchase/edi_purchase_order.py +++ b/addons/purchase/edi_purchase_order.py @@ -32,7 +32,8 @@ class purchase_order(osv.osv, ir_edi.edi): edi_struct = { 'company_id': True, # -> to be changed into partner 'name': True, - + 'partner_ref': True, + 'origin': True, 'date_order': True, 'partner_id': True, 'partner_address_id': True, #only one address needed @@ -50,12 +51,13 @@ class purchase_order(osv.osv, ir_edi.edi): 'order_line': { 'name': True, 'date_planned': True, - #SO: 'delay' : 'date_approve' - 'date_planned' - #PO: 'date_planned': 'date_approve' + 'delay' + #SO: 'delay' : 'date_order' - 'date_planned' + #PO: 'date_planned': 'date_order' + 'delay' 'product_id': True, 'product_uom': True, 'price_unit': True, + 'price_subtotal': True, 'product_qty': True, #SO: 'product_uom_qty' #PO: 'product_qty' diff --git a/addons/sale/edi_sale_order.py b/addons/sale/edi_sale_order.py index 1baad0f59b5..e444f1371eb 100644 --- a/addons/sale/edi_sale_order.py +++ b/addons/sale/edi_sale_order.py @@ -32,6 +32,8 @@ class sale_order(osv.osv, ir_edi.edi): edi_struct = { 'company_id': True, # -> to be changed into partner 'name': True, + 'client_order_ref': True, + 'origin': True, 'date_order': True, 'partner_id': True, 'partner_order_id': True, #only one address needed @@ -50,8 +52,8 @@ class sale_order(osv.osv, ir_edi.edi): #PO: No 'name': True, 'delay': True, - #SO: 'delay' : 'date_approve' - 'date_planned' - #PO: 'date_planned': 'date_approve' + 'delay' + #SO: 'delay' : 'date_order' - 'date_planned' + #PO: 'date_planned': 'date_order' + 'delay' 'product_id': True, 'product_uom': True, @@ -59,7 +61,7 @@ class sale_order(osv.osv, ir_edi.edi): 'product_uom_qty': True, #SO: 'product_uom_qty' #PO: 'product_qty' - + 'price_subtotal': True, 'discount': True, #SO: yes #PO: No diff --git a/addons/sale/edi_sale_order_data.xml b/addons/sale/edi_sale_order_data.xml index 66e38f40360..04683d1a230 100644 --- a/addons/sale/edi_sale_order_data.xml +++ b/addons/sale/edi_sale_order_data.xml @@ -4,7 +4,7 @@ context.update({'edi_web_sale_url_view': '%s/web/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) -if not object.partner_id.opt_out: self.pool.get('email.template').send_email(cr, +if not object.partner_id.opt_out: self.pool.get('email.template').send_mail(cr, uid, self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sale', 'email_template_edi_sale')[1], object.id, From f8d9aeaca5dc410121495a046b55ac99ad051fc1 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Fri, 16 Sep 2011 15:40:31 +0530 Subject: [PATCH 039/132] [IMP] edi: Imporve Email Template of sale,purchase bzr revid: hmo@tinyerp.com-20110916101031-3lspjfm35e958qbr --- addons/purchase/edi_purchase_order_data.xml | 10 ++++------ addons/sale/edi_sale_order_data.xml | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/addons/purchase/edi_purchase_order_data.xml b/addons/purchase/edi_purchase_order_data.xml index 7e6482b6ca1..02ecb24b9ba 100644 --- a/addons/purchase/edi_purchase_order_data.xml +++ b/addons/purchase/edi_purchase_order_data.xml @@ -36,30 +36,28 @@ if not object.partner_id.opt_out: self.pool.get('email.template').send_mail(cr, <div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); "> <p> Hello ${object.partner_address_id.name and ' ' or ''},</p> -<p> You can click on the following link to preview, print and pay invoice: <br/> +<p> You can click on the following link to preview purchase order: <br/> <a href="${object._context.get('edi_web_purchase_url_view')}">${object._context.get('edi_web_purchase_url_view')} </a> </p> -<p style="border-left: 1px solid #8e0000; margin-left: 30px;"> <strong>REFERENCES</strong><br /> Order number: <strong>${object.name}</strong><br /> Order amount: <strong>${object.amount_total} </strong><br /> Confirm date: ${object.date_approve or 'n/a'}<br /> Your contact: <a href="mailto:${object.validator.user_email or ''}?subject=Order%20${object.name}">${object.validator.name}</a></p> +<p style="border-left: 1px solid #8e0000; margin-left: 30px;"> <strong>REFERENCES</strong><br /> Order number: <strong>${object.name}</strong><br /> Order amount: <strong>${object.amount_total} </strong><br /> Order date: ${object.date_order or 'n/a'}<br /> Your contact: <a href="mailto:${object.validator.user_email or ''}?subject=Order%20${object.name}">${object.validator.name}</a></p> -${object.company_id.paypal_account and "<p>It is possible to pay with Paypal: <br/> <a href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Invoice%%20%s&invoice=%s&amount=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s\"><img src=\"https://www.paypalobjects.com/en_US/i/btn/btn_paynowCC_LG.gif\" style=\"margin-left: 100px; border: 0px; padding: 1px; text-decoration: none;\"/></a> </p>"%(object.company_id.paypal_account, object.name and object.name.replace('/','%2f') or '', object.name and object.name.replace('/','%2f') or '', object.amount_total) or ''} <p> If you have any question, do not hesitate to reply directly to this e-mail.</p> <p> Thank you for choosing OpenERP!<br /> </p> <div style="width: 375px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; overflow-x: hidden; overflow-y: hidden; zoom: 1; background-image: url(http://www.openerp.com/sites/default/files/red_gradient_bg.png); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(142, 0, 0); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-bottom-right-radius: 0px 0px; border-bottom-left-radius: 0px 0px; background-position: 0% 0%; background-repeat: repeat no-repeat; "> <h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 9px; padding-right: 14px; padding-bottom: 9px; padding-left: 14px; font-size: 12px; font-weight: normal; font-style: normal; color: rgb(255, 255, 255); "> <strong>${object.company_id.name}</strong></h3> </div> <div style="width: 347px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 12px; padding-right: 14px; padding-bottom: 12px; padding-left: 14px; overflow-x: hidden; overflow-y: hidden; zoom: 1; line-height: 16px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(242, 242, 242); "> <div> Contact:<a href="mailto:${object.validator.user_email or ''}?subject=Order%20${object.name}">${object.validator.name}</a></div> <div> </div> </div> </div> <p> </p> Hello ${object.partner_address_id.name and ' ' or ''}, -You can click on the following link to preview, print and pay invoice: +You can click on the following link to preview Purchase Order: ${object._context.get('edi_web_purchase_url_view') or 'n/a'} Order Number: *${object.name}* Amount: *${object.amount_total}* -Approve date: ${object.date_approve or 'n/a'} +Order date: ${object.date_order or 'n/a'} Your contact: ${object.validator.name} ${object.validator.user_email and '<%s>'%(object.validator.user_email) or ''} -${object.company_id.paypal_account and "It is possible to pay with Paypal: https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Invoice%%20%s&invoice=%s&amount=%s&currency_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s"%(object.company_id.paypal_account, object.name and object.name.replace('/','%2f') or '', object.name and object.name.replace('/','%2f') or '', object.amount_total) or ''} If you have any question, do not hesitate to reply directly to this e-mail. diff --git a/addons/sale/edi_sale_order_data.xml b/addons/sale/edi_sale_order_data.xml index 04683d1a230..33b65b4c031 100644 --- a/addons/sale/edi_sale_order_data.xml +++ b/addons/sale/edi_sale_order_data.xml @@ -36,30 +36,28 @@ if not object.partner_id.opt_out: self.pool.get('email.template').send_mail(cr, <div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); "> <p> Hello ${object.partner_invoice_id.name and ' ' or ''},</p> -<p> You can click on the following link to preview, print and pay invoice: <br/> +<p> You can click on the following link to preview sale order : <br/> <a href="${object._context.get('edi_web_sale_url_view')}">${object._context.get('edi_web_sale_url_view')} </a> </p> -<p style="border-left: 1px solid #8e0000; margin-left: 30px;"> <strong>REFERENCES</strong><br /> Order number: <strong>${object.name}</strong><br /> Order amount: <strong>${object.amount_total} </strong><br /> Confirm date: ${object.date_confirm or 'n/a'}<br /> Your contact: <a href="mailto:${object.user_id.user_email or ''}?subject=Order%20${object.name}">${object.user_id.name}</a></p> +<p style="border-left: 1px solid #8e0000; margin-left: 30px;"> <strong>REFERENCES</strong><br /> Order number: <strong>${object.name}</strong><br /> Order amount: <strong>${object.amount_total} </strong><br /> Order date: ${object.date_order or 'n/a'}<br /> Your contact: <a href="mailto:${object.user_id.user_email or ''}?subject=Order%20${object.name}">${object.user_id.name}</a></p> -${object.company_id.paypal_account and "<p>It is possible to pay with Paypal: <br/> <a href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Invoice%%20%s&invoice=%s&amount=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s\"><img src=\"https://www.paypalobjects.com/en_US/i/btn/btn_paynowCC_LG.gif\" style=\"margin-left: 100px; border: 0px; padding: 1px; text-decoration: none;\"/></a> </p>"%(object.company_id.paypal_account, object.name and object.name.replace('/','%2f') or '', object.name and object.name.replace('/','%2f') or '', object.amount_total) or ''} <p> If you have any question, do not hesitate to reply directly to this e-mail.</p> <p> Thank you for choosing OpenERP!<br /> </p> <div style="width: 375px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; overflow-x: hidden; overflow-y: hidden; zoom: 1; background-image: url(http://www.openerp.com/sites/default/files/red_gradient_bg.png); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(142, 0, 0); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-bottom-right-radius: 0px 0px; border-bottom-left-radius: 0px 0px; background-position: 0% 0%; background-repeat: repeat no-repeat; "> <h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 9px; padding-right: 14px; padding-bottom: 9px; padding-left: 14px; font-size: 12px; font-weight: normal; font-style: normal; color: rgb(255, 255, 255); "> <strong>${object.company_id.name}</strong></h3> </div> <div style="width: 347px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 12px; padding-right: 14px; padding-bottom: 12px; padding-left: 14px; overflow-x: hidden; overflow-y: hidden; zoom: 1; line-height: 16px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(242, 242, 242); "> <div> Contact:<a href="mailto:${object.user_id.user_email or ''}?subject=Order%20${object.name}">${object.user_id.name}</a></div> <div> </div> </div> </div> <p> </p> Hello ${object.partner_invoice_id.name and ' ' or ''}, -You can click on the following link to preview, print and pay invoice: +You can click on the following link to preview sale order: ${object._context.get('edi_web_sale_url_view') or 'n/a'} Order Number: *${object.name}* Amount: *${object.amount_total}* -Confirm date: ${object.date_confirm or 'n/a'} +Order date: ${object.date_order or 'n/a'} Your contact: ${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''} -${object.company_id.paypal_account and "It is possible to pay with Paypal: https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Invoice%%20%s&invoice=%s&amount=%s&currency_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s"%(object.company_id.paypal_account, object.name and object.name.replace('/','%2f') or '', object.name and object.name.replace('/','%2f') or '', object.amount_total) or ''} If you have any question, do not hesitate to reply directly to this e-mail. From b3398647231a9d7ef414e2185f160c44773ac312 Mon Sep 17 00:00:00 2001 From: "Hardik Ansodariy (OpenERP)" Date: Fri, 16 Sep 2011 17:45:54 +0530 Subject: [PATCH 040/132] [imp] bzr revid: han@tinyerp.com-20110916121554-t3wt2v075jl5141u --- addons/sale/edi_sale_order_data.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sale/edi_sale_order_data.xml b/addons/sale/edi_sale_order_data.xml index 66e38f40360..04683d1a230 100644 --- a/addons/sale/edi_sale_order_data.xml +++ b/addons/sale/edi_sale_order_data.xml @@ -4,7 +4,7 @@ context.update({'edi_web_sale_url_view': '%s/web/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) -if not object.partner_id.opt_out: self.pool.get('email.template').send_email(cr, +if not object.partner_id.opt_out: self.pool.get('email.template').send_mail(cr, uid, self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sale', 'email_template_edi_sale')[1], object.id, From 95b859a4f9d7af4069c53bde065bd4a860c729c2 Mon Sep 17 00:00:00 2001 From: "Hardik Ansodariy (OpenERP)" Date: Tue, 20 Sep 2011 14:28:46 +0530 Subject: [PATCH 041/132] [imp] set currency id bzr revid: han@tinyerp.com-20110920085846-dxdr6fbjtw7ltedy --- addons/purchase/edi_purchase_order.py | 3 ++- addons/sale/edi_sale_order.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/purchase/edi_purchase_order.py b/addons/purchase/edi_purchase_order.py index 31017daceb1..9119485bffc 100644 --- a/addons/purchase/edi_purchase_order.py +++ b/addons/purchase/edi_purchase_order.py @@ -73,12 +73,13 @@ class purchase_order(osv.osv, ir_edi.edi): if not edi_doc: continue edi_doc = edi_doc[0] + currency = order.company_id.currency_id # Add company info and address edi_company_document = company_pool.edi_export_address(cr, uid, [order.company_id], context=context)[order.company_id.id] edi_doc.update({ 'company_address': edi_company_document['company_address'], - 'currency_id': edi_company_document['currency_id'], + 'currency_id': currency and self.edi_m2o(cr, uid, currency, context=context) or False, #'company_logo': edi_company_document['company_logo'],#TODO }) edi_doc_list.append(edi_doc) diff --git a/addons/sale/edi_sale_order.py b/addons/sale/edi_sale_order.py index eddeb618926..69165de9b85 100644 --- a/addons/sale/edi_sale_order.py +++ b/addons/sale/edi_sale_order.py @@ -77,12 +77,12 @@ class sale_order(osv.osv, ir_edi.edi): if not edi_doc: continue edi_doc = edi_doc[0] - + currency = order.company_id.currency_id # Add company info and address edi_company_document = company_pool.edi_export_address(cr, uid, [order.company_id], context=context)[order.company_id.id] edi_doc.update({ 'company_address': edi_company_document['company_address'], - 'currency_id': edi_company_document['currency_id'], + 'currency_id': currency and self.edi_m2o(cr, uid, currency, context=context) or False, #'company_logo': edi_company_document['company_logo'],#TODO }) edi_doc_list.append(edi_doc) From dc5089ed1ceb82e326332465851fc68f97c34c9f Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Fri, 30 Sep 2011 23:09:26 +0200 Subject: [PATCH 042/132] [IMP] mail.message: pass alternative body to ir.mail_server when available bzr revid: odo@openerp.com-20110930210926-6bcqzgoxr6tmmnhs --- addons/mail/mail_message.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 621eef59da5..6037398fb7f 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -479,11 +479,22 @@ class mail_message(osv.osv): attachments = [] for attach in message.attachment_ids: attachments.append((attach.datas_fname, base64.b64decode(attach.datas))) + + body = message.body_html if message.subtype == 'html' else message.body_text + body_alternative = None + subtype_alternative = None + if message.subtype == 'html' and message.body_text: + # we have a plain text alternative prepared, pass it to + # build_message instead of letting it build one + body_alternative = message.body_text + subtype_alternative = 'plain' + msg = ir_mail_server.build_email( email_from=message.email_from, email_to=to_email(message.email_to), subject=message.subject, - body=message.body_html if message.subtype == 'html' else message.body_text, + body=body, + body_alternative=body_alternative, email_cc=to_email(message.email_cc), email_bcc=to_email(message.email_bcc), reply_to=message.reply_to, @@ -491,6 +502,7 @@ class mail_message(osv.osv): references = message.references, object_id=message.res_id and ('%s-%s' % (message.res_id,message.model)), subtype=message.subtype, + subtype_alternative=subtype_alternative, headers=message.headers and literal_eval(message.headers)) res = ir_mail_server.send_email(cr, uid, msg, mail_server_id=message.mail_server_id.id, From f463c4f0b5368ee349e93e5ff1f357a20f902f41 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Fri, 30 Sep 2011 23:09:54 +0200 Subject: [PATCH 043/132] [IMP] email.template.preview: preview HTML body too bzr revid: odo@openerp.com-20110930210954-8clkgqf4euf2xczl --- addons/email_template/wizard/email_template_preview.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/email_template/wizard/email_template_preview.py b/addons/email_template/wizard/email_template_preview.py index 650b714866d..4ef0e9fa156 100644 --- a/addons/email_template/wizard/email_template_preview.py +++ b/addons/email_template/wizard/email_template_preview.py @@ -87,6 +87,8 @@ class email_template_preview(osv.osv_memory): signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature description += '\n' + signature vals['body_text'] = description + if template.body_html: + vals['body_html'] = self.render_template(cr, uid, template.body_html, model, res_id, context) or '' vals['report_name'] = self.render_template(cr, uid, template.report_name, model, res_id, context) return {'value': vals} From ae744a7ad05d5b0288253ebdf16236e7b86b5e7c Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Fri, 30 Sep 2011 23:11:09 +0200 Subject: [PATCH 044/132] [IMP] email.template: pass proper subtype when HTML content is present bzr revid: odo@openerp.com-20110930211109-rgx9kmx6rghd5dsf --- addons/email_template/email_template.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index 1a8ba49594d..3eb0c60cd41 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -305,6 +305,7 @@ class email_template(osv.osv): 'attachment_ids': False, 'message_id': False, 'state': 'outgoing', + 'subtype': 'plain', } if not template_id: return values @@ -319,6 +320,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 From fcfffc7d76695af08f7f31aeebd18c3b6409ffd7 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Fri, 30 Sep 2011 23:21:54 +0200 Subject: [PATCH 045/132] [IMP] account: better auto-mail template and action definition bzr revid: odo@openerp.com-20110930212154-v32z6lve8b7pqm29 --- addons/account/edi_invoice_action_data.xml | 203 +++++++++++++++------ 1 file changed, 143 insertions(+), 60 deletions(-) diff --git a/addons/account/edi_invoice_action_data.xml b/addons/account/edi_invoice_action_data.xml index 517628b434c..6a2338fadbf 100644 --- a/addons/account/edi_invoice_action_data.xml +++ b/addons/account/edi_invoice_action_data.xml @@ -1,77 +1,160 @@ - - - - context.update({'edi_web_invoice_url_view': '%s/web/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) -if not object.partner_id.opt_out: self.pool.get('email.template').send_mail(cr, - uid, - self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'email_template_edi_invoice')[1], - object.id, - context=context) - - code - ir.actions.server - - True - EDI Document - Invoice - + + + + +try: + if not object.partner_id.opt_out: + web_root_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default="") - - - - - + # accessing the template will fail if it has been deleted - so we'll skip everything + tmpl = self.pool.get('ir.model.data').get_object(cr, uid, 'account', 'email_template_edi_invoice')[1] - - - - Mail Template of Invoice For EDI Document - ${object.user_id.user_email or ''} - ${object.user_id.user_email or ''} - - ${object.company_id.name} Invoice (Ref ${object.number or 'n/a' }) - ${object.address_invoice_id.email or ''} - + edi_token = self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0] + context.update(edi_web_invoice_url_view='%s/web/view_edi?db=%s&token=%s' %(web_root_url,cr.dbname, edi_token)) + self.pool.get('email.template').send_mail(cr, uid,tmpl.id,object.id,context=context) +except: + pass + + + code + ir.actions.server + + True + EDI Document - Invoice + + + + + + + + + + + + + Automated Invoice Notification Mail + ${object.user_id.user_email or object.company_id.email or 'noreply@localhost'} + ${object.company_id.name} Invoice (Ref ${object.number or 'n/a' }) + ${object.address_invoice_id.email or ''} + <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 ''},</p> -<p> You can click on the following link to preview, print and pay invoice: <br/> - <a href="${object._context.get('edi_web_invoice_url_view')}">${object._context.get('edi_web_invoice_url_view')} </a> -</p> - - -<p style="border-left: 1px solid #8e0000; margin-left: 30px;"> <strong>REFERENCES</strong><br /> Invoice number: <strong>${object.number}</strong><br /> Invoice amount: <strong>${object.amount_total} ${object.currency_id.name}</strong><br /> Invoice date: ${object.date_invoice or 'n/a'}<br /> Order reference: ${object.origin or 'n/a'}<br /> Your contact: <a href="mailto:${object.user_id.user_email or ''}?subject=Invoice%20${object.number}">${object.user_id.name}</a></p> - -${object.company_id.paypal_account and "<p>It is possible to pay with Paypal: <br/> <a href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Invoice%%20%s&invoice=%s&amount=%s&currency_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s\"><img src=\"https://www.paypalobjects.com/en_US/i/btn/btn_paynowCC_LG.gif\" style=\"margin-left: 100px; border: 0px; padding: 1px; text-decoration: none;\"/></a> </p>"%(object.company_id.paypal_account, object.number and object.number.replace('/','%2f') or '', object.number and object.number.replace('/','%2f') or '', object.amount_total, object.currency_id.name, object.currency_id.name) or ''} - -<p> If you have any question, do not hesitate to reply directly to this e-mail.</p> <p> Thank you for choosing OpenERP!<br /> </p> <div style="width: 375px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; overflow-x: hidden; overflow-y: hidden; zoom: 1; background-image: url(http://www.openerp.com/sites/default/files/red_gradient_bg.png); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(142, 0, 0); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-bottom-right-radius: 0px 0px; border-bottom-left-radius: 0px 0px; background-position: 0% 0%; background-repeat: repeat no-repeat; "> <h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 9px; padding-right: 14px; padding-bottom: 9px; padding-left: 14px; font-size: 12px; font-weight: normal; font-style: normal; color: rgb(255, 255, 255); "> <strong>${object.company_id.name}</strong></h3> </div> <div style="width: 347px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 12px; padding-right: 14px; padding-bottom: 12px; padding-left: 14px; overflow-x: hidden; overflow-y: hidden; zoom: 1; line-height: 16px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(242, 242, 242); "> <div> Contact:<a href="mailto:${object.user_id.user_email or ''}?subject=Invoice%20${object.number}">${object.user_id.name}</a></div> <div> </div> </div> </div> <p> </p> + <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 amount: <strong>${object.amount_total} ${object.currency_id.name}</strong><br /> +   Invoice date: ${object.date_invoice or 'n/a'}<br /> +   Order reference: ${object.origin or 'n/a'}<br /> +   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-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(142, 0, 0); border-radius: 2px 2px; background-position: 0% 0%; background-repeat: repeat no-repeat;" + href="${ctx.get('edi_web_invoice_url_view') or ''}">View Invoice</a> + + % if object.company_id.paypal_account: + <br/> + <p>It is also possible to directly pay with Paypal:</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-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(142, 0, 0); border-radius: 2px 2px; background-position: 0% 0%; background-repeat: repeat no-repeat;" + href="${"https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Invoice%%20%s&invoice=%s&amount=%s&currency_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s"%(object.company_id.paypal_account,object.number and object.number.replace('/','%2f') or '', object.number and object.number.replace('/','%2f') or '', object.amount_total, object.currency_id.name, object.currency_id.name)}" + >Pay with Paypal</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-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; overflow-x: hidden; overflow-y: hidden; zoom: 1; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(142, 0, 0); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-bottom-right-radius: 0px 0px; border-bottom-left-radius: 0px 0px; background-position: 0% 0%; background-repeat: repeat no-repeat; "> + <h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 9px; padding-right: 14px; padding-bottom: 9px; padding-left: 14px; font-size: 12px; font-weight: normal; font-style: normal; color: rgb(255, 255, 255); "> + <strong style="text-transform:uppercase;">${object.company_id.name}</strong></h3> + </div> + <div style="width: 347px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 12px; padding-right: 14px; padding-bottom: 12px; padding-left: 14px; overflow-x: hidden; overflow-y: hidden; zoom: 1; line-height: 16px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(242, 242, 242); "> + <span style="color: rgb(38, 72, 149); 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> - -Hello ${object.address_invoice_id.name and ' ' or ''}, + +Hello${object.address_invoice_id.name and ' ' or ''}${object.address_invoice_id.name or ''}, -You can click on the following link to preview, print and pay invoice: +A new invoice is available for ${object.partner_id.name}: + | Invoice Number: *${object.number}* + | Amount: *${object.amount_total} ${object.currency_id.name}* + | Invoice date: ${object.date_invoice or 'n/a'} + | Order reference: ${object.origin or 'n/a'} + | Your contact: ${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''} - ${object._context.get('edi_web_invoice_url_view') or 'n/a'} +You can view the invoice document, download it and pay online using the following link: + ${ctx.get('edi_web_invoice_url_view') or 'n/a'} -Invoice Number: *${object.number}* -Amount: *${object.amount_total} ${object.currency_id.name}* -Invoice date: ${object.date_invoice or 'n/a'} -Order reference: ${object.origin or 'n/a'} -Your contact: ${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''} +% if object.company_id.paypal_account: +It is also possible to directly pay with Paypal: + ${"https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Invoice%%20%s&invoice=%s&amount=%s&currency_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s"%(object.company_id.paypal_account, object.number and object.number.replace('/','%2f') or '', object.number and object.number.replace('/','%2f') or '', object.amount_total, object.currency_id.name, object.currency_id.name)} +% endif -${object.company_id.paypal_account and "It is possible to pay with Paypal: https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Invoice%%20%s&invoice=%s&amount=%s&currency_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s"%(object.company_id.paypal_account, object.number and object.number.replace('/','%2f') or '', object.number and object.number.replace('/','%2f') or '', object.amount_total, object.currency_id.name, object.currency_id.name) or ''} +If you have any question, do not hesitate to contact us. -If you have any question, do not hesitate to reply directly to this e-mail. -Thank you for choosing our service! +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} -Contact: ${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''} - - - - +% 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 + + + From d59572ccc2bf4d8abfef2e2cac500bad033020ea Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 4 Oct 2011 18:43:39 +0200 Subject: [PATCH 046/132] [ADD] edi: moved from server into separate module bzr revid: odo@openerp.com-20111004164339-4fxlpxjadsm499kb --- addons/edi/__init__.py | 23 + addons/edi/__openerp__.py | 48 ++ addons/edi/edi_service.py | 70 +++ addons/edi/models/__init__.py | 24 + addons/edi/models/edi.py | 589 +++++++++++++++++++++++++ addons/edi/models/res_company.py | 121 +++++ addons/edi/models/res_partner.py | 64 +++ addons/edi/test/test_edi_documents.yml | 43 ++ 8 files changed, 982 insertions(+) create mode 100644 addons/edi/__init__.py create mode 100644 addons/edi/__openerp__.py create mode 100644 addons/edi/edi_service.py create mode 100644 addons/edi/models/__init__.py create mode 100644 addons/edi/models/edi.py create mode 100644 addons/edi/models/res_company.py create mode 100644 addons/edi/models/res_partner.py create mode 100644 addons/edi/test/test_edi_documents.yml diff --git a/addons/edi/__init__.py b/addons/edi/__init__.py new file mode 100644 index 00000000000..4e266ec8443 --- /dev/null +++ b/addons/edi/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2011 OpenERP S.A. +# +# 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 . +# +############################################################################## + +import models +import edi_service \ No newline at end of file diff --git a/addons/edi/__openerp__.py b/addons/edi/__openerp__.py new file mode 100644 index 00000000000..d262a5e0434 --- /dev/null +++ b/addons/edi/__openerp__.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2011 OpenERP S.A. +# +# 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 . +# +############################################################################## +{ + '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'], + 'test': [ + 'test/partner_edi_test.yml', + ], + 'installable': True, + 'active': False, + #'certificate': TODO +} + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/addons/edi/edi_service.py b/addons/edi/edi_service.py new file mode 100644 index 00000000000..35b6d32e272 --- /dev/null +++ b/addons/edi/edi_service.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2011 OpenERP S.A. +# +# 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 . +# +############################################################################## +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): + registry = openerp.modules.registry.RegistryManager.get(db_name) + assert registry, 'Unknown database %s' % db_name + edi_document = registry['edi.document'] + cr = registry.db.cursor() + try: + 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) + cr.rollback() + 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, uid, passwd, edi_document, context=None): + return self._edi_dispatch(db_name, 'import_edi', 1, edi_document, None) + + def exp_import_edi_url(self, db, uid, passwd, edi_url, context=None): + return self._edi_dispatch(db_name, 'import_edi', 1, 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() \ No newline at end of file diff --git a/addons/edi/models/__init__.py b/addons/edi/models/__init__.py new file mode 100644 index 00000000000..201f1c6f073 --- /dev/null +++ b/addons/edi/models/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2011 OpenERP S.A. +# +# 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 . +# +############################################################################## + +import edi +import res_partner +import res_company \ No newline at end of file diff --git a/addons/edi/models/edi.py b/addons/edi/models/edi.py new file mode 100644 index 00000000000..734fa61df8f --- /dev/null +++ b/addons/edi/models/edi.py @@ -0,0 +1,589 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2011 OpenERP S.A. +# +# 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 . +# +############################################################################## + +import base64 +import hashlib +import json +import logging +import re +import time +import urllib2 + +import openerp +import openerp.release as release +from osv import osv,fields +from tools.translate import _ +from tools.parse_version import parse_version + +EXTERNAL_ID_PATTERN = re.compile(r'^(\S+:)?(\S+?)\.(\S+)$') + +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) and match.group(1)[:-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 version_tuple(): + return parse_version(release.version) + +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) pairs containing the model and database ID + of all records that were imported in the system + """ + ir_module = self.pool.get('ir.module.module') + res = [] + for edi_document in edi_documents: + module = edi_document.get('__module') + 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. " + "The OpenERP configuration assistant will help with this if you are connected as an administrator.")%(module,)) + model = edi_document.get('__model') + assert model, '__model attribute is required in each EDI document' + model_obj = self.pool.get(model) + record_id = model_obj.edi_import(cr, uid, edi_document, context=context) + res.append((model, record_id)) + 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.""" + + # 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 = self.get_external_id(cr, uid, [record.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._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 + assert module == record._module, 'Module mismatch between record and its current'\ + 'external ID' + # ID is unique cross-db thanks to db_uuid + module = "%s:%s" % (module, db_uuid) + + return '%s.%s' % (module, ext_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' : [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 = [] + version = version_tuple() + for record in records: + attachment_ids = ir_attachment.search(cr, uid, [('res_model','=', record._name), ('res_id', '=', record.id)]) + 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, + }) + 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._module, + '__version': version, + '__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: + continue + #if _fields[field].has_key('function') or _fields[field].has_key('related_columns'): + # # Do not Export Function Fields and related fields + # 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_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_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'] + ext_module = '%s:%s' % (module, ext_id_members['db_uuid']) + data_ids = ir_model_data.search(cr, uid, [('model','=',model), + ('name','=',ext_id), + ('module','in',[ext_module,module])]) + 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]", self._name, 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 + res_id, name = self.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, + applying the following rules. + + All relationship fields are exported in a special way, and provide their own + unique identifier, so that we can avoid duplication of records when importing. + + #. Many2One + See :meth:`~.edi_import_relation` + + #. One2Many + O2M fields are always exported as a list of dicts, where each dict corresponds + to a full EDI record. The import should not update existing records + if they already exist, it should only link them to the parent object, in this + fashion: + * First import the parent object, using the usual procedire + * Look for a record that matches the db_id provided in the __id field. If + found, keep the corresponding database id, and connect it to the parent. + * If not found via db_id, create a new entry using the same method that + imports a full EDI record, grab the resulting db id, and use it to + connect to the parent. + + #: Many2Many + M2M fields are always exported as a list of pairs similar to M2O. + For each pair in the M2M: + * Perform the same steps as for a Many2One (See :meth:`~.edi_import_relation`) + * After finding the database ID of the final record in the database, + connect it to the parent record. + """ + assert self._name == edi_document['__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_id = self._edi_get_object_by_external_id(cr, uid, ext_id_members['full'], self._name, context=context) + if existing_id: + _logger.info("'%s' EDI Document with ID '%s' is already known, skipping import!", self._name, ext_id_members['full']) + return + + 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 = self._all_columns[field_name].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: \ No newline at end of file diff --git a/addons/edi/models/res_company.py b/addons/edi/models/res_company.py new file mode 100644 index 00000000000..0641ff0e474 --- /dev/null +++ b/addons/edi/models/res_company.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2011 Tiny SPRL (). +# +# 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 . +# +############################################################################## + +#from osv import fields,osv + +# TODO: CHECK below, seems useless +# +#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_import_as_partner(self, cr, uid, edi_document, values=None, context=None): +# """ +# import company as a new partner +# company_address data used to add address to new partner +# +# edi_document is a dict to have company datas +# edi_document = { +# 'company_address': { +# 'street': True, +# 'street2': True, +# 'zip': True, +# 'city': True, +# 'state_id': True, +# 'country_id': True, +# 'email': True, +# 'phone': True, +# +# }, +# 'company_id': True, +# } +# values is a dict to have other datas of partner which are need to import of partner record +# values = { +# 'customer': True, +# 'supplier': True, +# } +# """ +# if values is None: +# values = {} +# partner_model = 'res.partner' +# partner_pool = self.pool.get(partner_model) +# xml_id = edi_document['company_id'][0] +# company_address = edi_document.get('company_address', False) +# partner_name = edi_document['company_id'][1] +# partner = partner_pool.edi_get_object(cr, uid, xml_id, partner_model, context=context) +# if not partner: +# partner = partner_pool.edi_get_object_by_name(cr, uid, partner_name, partner_model, context=context) +# +# if partner: + #FIXME +# record_xml = partner_pool._get_external_id(cr, uid, [partner.id], context=context) +# if record_xml: +# module, xml_id = record_xml +# xml_id = '%s.%s' % (module, xml_id) +# +# edi_document_partner = { +# '__model': partner_model, +# '__id' : xml_id, +# 'name' : partner_name, +# } +# if company_address: +# edi_document_partner['address'] = [company_address] +# +# edi_document_partner.update(values) +# return partner_pool.edi_import(cr, uid, edi_document_partner, context=context) +# +# def edi_export_address(self, cr, uid, records, edi_address_struct=None, context=None): +# """Return a dict representation of the address of each company record, suitable for +# inclusion in an EDI document, and matching the given edi_address_struct if provided. +# +# :param list(browse_record) records: list of companies to export +# :rtype: list(dict) +# :return: list of dicts, where each dict contains the address representation for +# the company record as the same index in ``records``. +# """ +# if context is None: +# context = {} +# res_partner = self.pool.get('res.partner') +# res_partner_address = self.pool.get('res.partner.address') +# if edi_address_struct is None: +# edi_address_struct = { +# 'street': True, +# 'street2': True, +# 'zip': True, +# 'city': True, +# 'state_id': True, +# 'country_id': True, +# 'email': True, +# 'phone': True, +# } +# results = [] +# for company in records: +# res = res_partner.address_get(cr, uid, [company.partner_id.id], ['default', 'contact', 'invoice']) +# addr_id = res['invoice'] or res['contact'] or res['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=ctx)[0] +# results.append(result) +# return results + diff --git a/addons/edi/models/res_partner.py b/addons/edi/models/res_partner.py new file mode 100644 index 00000000000..e73badf226c --- /dev/null +++ b/addons/edi/models/res_partner.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). +# +# 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 . +# +############################################################################## +from osv import fields,osv +from edi import EDIMixin + +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, + dict(RES_PARTNER_EDI_STRUCT), + context=context) + +class res_partner_address(osv.osv, EDIMixin): + _inherit = "res.partner.address" + + def edi_export(self, cr, uid, records, edi_struct=None, context=None): + return super(res_partner_address,self).edi_export(cr, uid, records, + dict(RES_PARTNER_ADDRESS_EDI_STRUCT), + context=context) + +class res_partner_bank(osv.osv, EDIMixin): + _inherit = "res.partner.bank" + diff --git a/addons/edi/test/test_edi_documents.yml b/addons/edi/test/test_edi_documents.yml new file mode 100644 index 00000000000..c9695af73e1 --- /dev/null +++ b/addons/edi/test/test_edi_documents.yml @@ -0,0 +1,43 @@ +- + 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: ir.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', + 'content': 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAAKCAYAAACE2W/HAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9oIDQ84BkjLWAYAAACNSURBVCjPjZKxDUJBDEOf/wbUbEHDhkxAzRjUDECBhMQYjIApuPBNpK+PpUinOPYll5Nt1iCJXifbSPpJZtHgXLUdu0FWpMEt87a/xtsmqjjNetPFyhuWYFuj7RcgQBNwXNGdw2CqY85yXWi5z7YBnmRyEHvg0YSXrLE9P30nwugOHHr+s5va4x+fofAGm1+JjnJICm0AAAAASUVORK5CYII=', + } + 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)) \ No newline at end of file From b57ad41b0bff288abb972d75174331ddbfbd4f46 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 4 Oct 2011 22:57:16 +0200 Subject: [PATCH 047/132] [IMP] edi: finish moving edi into new addon bzr revid: odo@openerp.com-20111004205716-u3pdb9th4ma3zkvx --- addons/edi/__openerp__.py | 5 +- addons/edi/models/edi.py | 8 +- addons/edi/models/res_company.py | 74 ++++++++----------- addons/edi/models/res_partner.py | 7 +- addons/edi/security/ir.model.access.csv | 3 + ...edi_documents.yml => edi_partner_test.yml} | 3 +- 6 files changed, 46 insertions(+), 54 deletions(-) create mode 100644 addons/edi/security/ir.model.access.csv rename addons/edi/test/{test_edi_documents.yml => edi_partner_test.yml} (93%) diff --git a/addons/edi/__openerp__.py b/addons/edi/__openerp__.py index d262a5e0434..414dff0e52c 100644 --- a/addons/edi/__openerp__.py +++ b/addons/edi/__openerp__.py @@ -37,8 +37,11 @@ technical OpenERP documentation at http://doc.openerp.com 'author': 'OpenERP SA', 'website': 'http://www.openerp.com', 'depends': ['base'], + 'data': [ + 'security/ir.model.access.csv', + ], 'test': [ - 'test/partner_edi_test.yml', + 'test/edi_partner_test.yml', ], 'installable': True, 'active': False, diff --git a/addons/edi/models/edi.py b/addons/edi/models/edi.py index 734fa61df8f..6191275b2a6 100644 --- a/addons/edi/models/edi.py +++ b/addons/edi/models/edi.py @@ -245,7 +245,7 @@ class EDIMixin(object): 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._module, db_uuid) + 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, @@ -257,8 +257,8 @@ class EDIMixin(object): module, ext_id = ext_id.split('.') if not ':' in module: # this record was not previously EDI-imported - assert module == record._module, 'Module mismatch between record and its current'\ - 'external ID' + assert module == record._original_module,\ + 'Mismatching module: expected %s, got %s' % (module, record._original_module) # ID is unique cross-db thanks to db_uuid module = "%s:%s" % (module, db_uuid) @@ -302,7 +302,7 @@ class EDIMixin(object): '__id': ext_id, '__last_update': last_update_for(record), '__model' : record._name, - '__module' : record._module, + '__module' : record._original_module, '__version': version, '__attachments': attachments, } diff --git a/addons/edi/models/res_company.py b/addons/edi/models/res_company.py index 0641ff0e474..a187532347f 100644 --- a/addons/edi/models/res_company.py +++ b/addons/edi/models/res_company.py @@ -19,16 +19,39 @@ # ############################################################################## -#from osv import fields,osv +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, records, edi_address_struct=None, context=None): + """Returns a dict representation of the address of each company record, suitable for + inclusion in an EDI document, and matching the given edi_address_struct if provided. + + :param list(browse_record) records: list of companies to export + :rtype: list(dict) + :return: list of dicts, where each dict contains the address representation for + the company record as the same index in ``records``. + """ + if context is None: + context = {} + res_partner = self.pool.get('res.partner') + res_partner_address = self.pool.get('res.partner.address') + results = [] + for company in records: + res = res_partner.address_get(cr, uid, [company.partner_id.id], ['default', 'contact', 'invoice']) + addr_id = res['invoice'] or res['contact'] or res['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=ctx)[0] + resuls.append(result) + return [] # TODO: CHECK below, seems useless -# -#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_import_as_partner(self, cr, uid, edi_document, values=None, context=None): # """ # import company as a new partner @@ -84,38 +107,3 @@ # edi_document_partner.update(values) # return partner_pool.edi_import(cr, uid, edi_document_partner, context=context) # -# def edi_export_address(self, cr, uid, records, edi_address_struct=None, context=None): -# """Return a dict representation of the address of each company record, suitable for -# inclusion in an EDI document, and matching the given edi_address_struct if provided. -# -# :param list(browse_record) records: list of companies to export -# :rtype: list(dict) -# :return: list of dicts, where each dict contains the address representation for -# the company record as the same index in ``records``. -# """ -# if context is None: -# context = {} -# res_partner = self.pool.get('res.partner') -# res_partner_address = self.pool.get('res.partner.address') -# if edi_address_struct is None: -# edi_address_struct = { -# 'street': True, -# 'street2': True, -# 'zip': True, -# 'city': True, -# 'state_id': True, -# 'country_id': True, -# 'email': True, -# 'phone': True, -# } -# results = [] -# for company in records: -# res = res_partner.address_get(cr, uid, [company.partner_id.id], ['default', 'contact', 'invoice']) -# addr_id = res['invoice'] or res['contact'] or res['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=ctx)[0] -# results.append(result) -# return results - diff --git a/addons/edi/models/res_partner.py b/addons/edi/models/res_partner.py index e73badf226c..c4d6b5297d8 100644 --- a/addons/edi/models/res_partner.py +++ b/addons/edi/models/res_partner.py @@ -18,7 +18,7 @@ # along with this program. If not, see . # ############################################################################## -from osv import fields,osv +from osv import fields,osv from edi import EDIMixin RES_PARTNER_ADDRESS_EDI_STRUCT = { @@ -48,7 +48,7 @@ class res_partner(osv.osv, EDIMixin): def edi_export(self, cr, uid, records, edi_struct=None, context=None): return super(res_partner,self).edi_export(cr, uid, records, - dict(RES_PARTNER_EDI_STRUCT), + edi_struct or dict(RES_PARTNER_EDI_STRUCT), context=context) class res_partner_address(osv.osv, EDIMixin): @@ -56,9 +56,8 @@ class res_partner_address(osv.osv, EDIMixin): def edi_export(self, cr, uid, records, edi_struct=None, context=None): return super(res_partner_address,self).edi_export(cr, uid, records, - dict(RES_PARTNER_ADDRESS_EDI_STRUCT), + edi_struct or dict(RES_PARTNER_ADDRESS_EDI_STRUCT), context=context) class res_partner_bank(osv.osv, EDIMixin): _inherit = "res.partner.bank" - diff --git a/addons/edi/security/ir.model.access.csv b/addons/edi/security/ir.model.access.csv new file mode 100644 index 00000000000..f1942397ac5 --- /dev/null +++ b/addons/edi/security/ir.model.access.csv @@ -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 \ No newline at end of file diff --git a/addons/edi/test/test_edi_documents.yml b/addons/edi/test/edi_partner_test.yml similarity index 93% rename from addons/edi/test/test_edi_documents.yml rename to addons/edi/test/edi_partner_test.yml index c9695af73e1..e2e497f1344 100644 --- a/addons/edi/test/test_edi_documents.yml +++ b/addons/edi/test/edi_partner_test.yml @@ -6,7 +6,7 @@ with an attached file, check the result, the alter the data and reimport it. - - !python {model: ir.edi.document}: | + !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'))]) @@ -16,7 +16,6 @@ # 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 From 77180a5bccd77b0a6ccdc972682689b76168e4a6 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 5 Oct 2011 02:33:38 +0200 Subject: [PATCH 048/132] [IMP] edi: bugfixes/cleanup after more review bzr revid: odo@openerp.com-20111005003338-6ol0kd8espdfaq32 --- addons/edi/__init__.py | 3 +- addons/edi/models/edi.py | 20 ++++++-- addons/edi/models/res_company.py | 87 +++++--------------------------- 3 files changed, 29 insertions(+), 81 deletions(-) diff --git a/addons/edi/__init__.py b/addons/edi/__init__.py index 4e266ec8443..4a7808548d2 100644 --- a/addons/edi/__init__.py +++ b/addons/edi/__init__.py @@ -20,4 +20,5 @@ ############################################################################## import models -import edi_service \ No newline at end of file +import edi_service +from models.edi import EDIMixin, edi_document \ No newline at end of file diff --git a/addons/edi/models/edi.py b/addons/edi/models/edi.py index 6191275b2a6..2394dd7012e 100644 --- a/addons/edi/models/edi.py +++ b/addons/edi/models/edi.py @@ -206,6 +206,11 @@ class EDIMixin(object): ``edi_import()`` and ``edi_export()`` methods to implement their specific behavior, based on the primitives provided by this mixin.""" + def _edi_requires_attributes(self, attributes, edi_document): + for attribute in attributes: + assert edi_document.get(attribute),\ + 'Attribute `%s` is required in %s EDI documents' % (attribute, self._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, @@ -241,7 +246,7 @@ class EDIMixin(object): """ 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 = self.get_external_id(cr, uid, [record.id])[record.id] + 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) @@ -454,9 +459,13 @@ class EDIMixin(object): module = ext_id_members['module'] ext_id = ext_id_members['id'] ext_module = '%s:%s' % (module, ext_id_members['db_uuid']) + modules = [ext_module] + if ext_id_members['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',[ext_module,module])]) + ('module','in',modules)]) if data_ids: model = self.pool.get(model) data = ir_model_data.browse(cr, uid, data_ids[0], context=context) @@ -476,7 +485,7 @@ class EDIMixin(object): 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]", self._name, external_id, value) + _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: @@ -488,7 +497,8 @@ class EDIMixin(object): _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 - res_id, name = self.name_create(cr, uid, value, context=context) + 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) @@ -537,7 +547,7 @@ class EDIMixin(object): existing_id = self._edi_get_object_by_external_id(cr, uid, ext_id_members['full'], self._name, context=context) if existing_id: _logger.info("'%s' EDI Document with ID '%s' is already known, skipping import!", self._name, ext_id_members['full']) - return + return existing_id.id record_values = {} o2m_todo = {} # o2m values are processed after their parent already exists diff --git a/addons/edi/models/res_company.py b/addons/edi/models/res_company.py index a187532347f..b81f5aa1d1f 100644 --- a/addons/edi/models/res_company.py +++ b/addons/edi/models/res_company.py @@ -27,83 +27,20 @@ class res_company(osv.osv): itself is not EDI-exportable""" _inherit = "res.company" - def edi_export_address(self, cr, uid, records, edi_address_struct=None, context=None): - """Returns a dict representation of the address of each company record, suitable for + 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 list(browse_record) records: list of companies to export - :rtype: list(dict) - :return: list of dicts, where each dict contains the address representation for - the company record as the same index in ``records``. + :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 """ - if context is None: - context = {} res_partner = self.pool.get('res.partner') res_partner_address = self.pool.get('res.partner.address') - results = [] - for company in records: - res = res_partner.address_get(cr, uid, [company.partner_id.id], ['default', 'contact', 'invoice']) - addr_id = res['invoice'] or res['contact'] or res['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=ctx)[0] - resuls.append(result) - return [] - -# TODO: CHECK below, seems useless -# def edi_import_as_partner(self, cr, uid, edi_document, values=None, context=None): -# """ -# import company as a new partner -# company_address data used to add address to new partner -# -# edi_document is a dict to have company datas -# edi_document = { -# 'company_address': { -# 'street': True, -# 'street2': True, -# 'zip': True, -# 'city': True, -# 'state_id': True, -# 'country_id': True, -# 'email': True, -# 'phone': True, -# -# }, -# 'company_id': True, -# } -# values is a dict to have other datas of partner which are need to import of partner record -# values = { -# 'customer': True, -# 'supplier': True, -# } -# """ -# if values is None: -# values = {} -# partner_model = 'res.partner' -# partner_pool = self.pool.get(partner_model) -# xml_id = edi_document['company_id'][0] -# company_address = edi_document.get('company_address', False) -# partner_name = edi_document['company_id'][1] -# partner = partner_pool.edi_get_object(cr, uid, xml_id, partner_model, context=context) -# if not partner: -# partner = partner_pool.edi_get_object_by_name(cr, uid, partner_name, partner_model, context=context) -# -# if partner: - #FIXME -# record_xml = partner_pool._get_external_id(cr, uid, [partner.id], context=context) -# if record_xml: -# module, xml_id = record_xml -# xml_id = '%s.%s' % (module, xml_id) -# -# edi_document_partner = { -# '__model': partner_model, -# '__id' : xml_id, -# 'name' : partner_name, -# } -# if company_address: -# edi_document_partner['address'] = [company_address] -# -# edi_document_partner.update(values) -# return partner_pool.edi_import(cr, uid, edi_document_partner, context=context) -# + addresses = res_partner.address_get(cr, uid, [company.partner_id.id], ['default', 'contact', 'invoice']) + addr_id = addresses['invoice'] or addresses['contact'] or addresses['default'] + if addr_id: + address = res_partner_address.browse(cr, uid, addr_id, context=context) + return res_partner_address.edi_export(cr, uid, [address], edi_struct=edi_address_struct, context=context)[0] + return {} From 05497056dec589962b6b0679dede81d6f1600328 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 5 Oct 2011 02:34:09 +0200 Subject: [PATCH 049/132] [IMP] account: edi export/import review bzr revid: odo@openerp.com-20111005003409-7umxxyebrh6jf8r2 --- addons/account/__init__.py | 2 +- addons/account/__openerp__.py | 4 +- addons/account/edi/__init__.py | 22 +++ .../{edi_invoice.py => edi/invoice.py} | 152 +++++++----------- .../invoice_action_data.xml} | 100 ++++++------ addons/account/test/test_edi_invoice.yml | 150 +++++++---------- 6 files changed, 189 insertions(+), 241 deletions(-) create mode 100644 addons/account/edi/__init__.py rename addons/account/{edi_invoice.py => edi/invoice.py} (59%) rename addons/account/{edi_invoice_action_data.xml => edi/invoice_action_data.xml} (53%) diff --git a/addons/account/__init__.py b/addons/account/__init__.py index 7ce59d3830d..2975177ea51 100644 --- a/addons/account/__init__.py +++ b/addons/account/__init__.py @@ -35,5 +35,5 @@ import product import sequence import company import res_currency -import edi_invoice +import edi # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index 61e1805b211..77f0f47569a 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -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", "email_template"], + "depends" : ["base_setup", "product", "analytic", "process", "board", "edi", "email_template"], 'update_xml': [ 'security/account_security.xml', 'security/ir.model.access.csv', @@ -124,7 +124,7 @@ module named account_voucher. 'board_account_view.xml', "wizard/account_report_profit_loss_view.xml", "wizard/account_report_balance_sheet_view.xml", - "edi_invoice_action_data.xml", + "edi/invoice_action_data.xml", "account_bank_view.xml", ], 'demo_xml': [ diff --git a/addons/account/edi/__init__.py b/addons/account/edi/__init__.py new file mode 100644 index 00000000000..d86e8a91bf3 --- /dev/null +++ b/addons/account/edi/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2011 OpenERP S.A. +# +# 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 . +# +############################################################################## + +import invoice diff --git a/addons/account/edi_invoice.py b/addons/account/edi/invoice.py similarity index 59% rename from addons/account/edi_invoice.py rename to addons/account/edi/invoice.py index db09e7ab6aa..de2a4e76c24 100644 --- a/addons/account/edi_invoice.py +++ b/addons/account/edi/invoice.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2009 Tiny SPRL (). +# OpenERP, Open Source Business Applications +# Copyright (c) 2011 OpenERP S.A. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -20,10 +20,10 @@ ############################################################################## from osv import fields, osv, orm -from base.ir import ir_edi +from edi import EDIMixin from tools.translate import _ -class account_invoice(osv.osv, ir_edi.edi): +class account_invoice(osv.osv, EDIMixin): _inherit = 'account.invoice' def edi_export(self, cr, uid, records, edi_struct=None, context=None): @@ -35,24 +35,19 @@ class account_invoice(osv.osv, ir_edi.edi): 'type': True, # -> reversed at import 'internal_number': True, # -> reference at import 'comment': True, - 'reference': True, - 'amount_untaxed': True, - 'amount_tax': True, - 'amount_total': True, - 'reconciled': True, 'date_invoice': True, 'date_due': True, 'partner_id': True, 'address_invoice_id': True, #only one address needed 'payment_term': True, - 'currency_id': True, + 'currency_id': True, # TODO: should perhaps include sample rate + rounding 'invoice_line': { 'name': True, 'origin': True, 'uos_id': True, 'product_id': True, 'price_unit': True, - 'price_subtotal': True, + #'price_subtotal': True, 'quantity': True, 'discount': True, 'note': True, @@ -66,40 +61,20 @@ class account_invoice(osv.osv, ir_edi.edi): 'base_amount': True, 'tax_amount': True, }, - #'paid': True, } - company_pool = self.pool.get('res.company') + res_company = self.pool.get('res.company') edi_doc_list = [] for invoice in records: - # Get EDI doc based on struct. The result will also contain all metadata fields and attachments. - edi_doc = super(account_invoice,self).edi_export(cr, uid, [invoice], edi_struct, context) - if not edi_doc: - continue - edi_doc = edi_doc[0] - - # Add company info and address - edi_company_document = company_pool.edi_export_address(cr, uid, [invoice.company_id], context=context)[invoice.company_id.id] + edi_doc = super(account_invoice,self).edi_export(cr, uid, [invoice], edi_struct, context)[0] edi_doc.update({ - 'company_address': edi_company_document['company_address'], - #'company_logo': edi_company_document['company_logo'],#TODO + 'company_address': res_company.edi_export_address(cr, uid, invoice.company_id, context=context), + #'company_logo': #TODO }) edi_doc_list.append(edi_doc) return edi_doc_list - def get_invoice_journal(self, cr, uid, invoice_type, context=None): - if context is None: - context = {} - account_journal_pool = self.pool.get('account.journal') - journal_context = context.copy() - journal_context.update({'type':invoice_type}) - journal_id = self._get_journal(cr, uid, context=journal_context) - journal = False - if journal_id: - journal = account_journal_pool.browse(cr, uid, journal_id, context=context) - return journal - - def get_tax_account(self, cr, uid, invoice_type='out_invoice', context=None): - #TOCHECK: should select account of output VAT for Customer Invoice and Input VAT for Supplier Invoice + 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 @@ -107,7 +82,7 @@ class account_invoice(osv.osv, ir_edi.edi): tax_account = account_pool.browse(cr, uid, account_ids[0]) return tax_account - def get_invoice_account(self, cr, uid, partner_id, invoice_type, context=None): + 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'): @@ -116,45 +91,47 @@ class account_invoice(osv.osv, ir_edi.edi): invoice_account = partner.property_account_payable return invoice_account - def get_product_account(self, cr, uid, product_id, invoice_type, context=None): + 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) - account = False if invoice_type in ('out_invoice','out_refund'): - account = product.product_tmpl_id.property_account_income - if not account: - account = product.categ_id.property_account_income_categ + account = product.property_account_income or product.categ_id.property_account_income_categ else: - account = product.product_tmpl_id.property_account_expense - if not account: - account = product.categ_id.property_account_expense_categ + 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): - partner_address_pool = self.pool.get('res.partner.address') - partner_pool = self.pool.get('res.partner') - company_pool = self.pool.get('res.company') + def _edi_import_company(self, cr, uid, edi_document, context=None): + 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') - # import company as a new partner, if type==in then supplier=1, else customer=1 - # company_address data used to add address to new partner + # imported company = new partner + company_id, company_name = edi_document['company_id'] + partner_id = self.edi_import_relation(cr, uid, 'res.partner', company_name, + company_id, context=context) invoice_type = edi_document['type'] partner_value = {} - if invoice_type in ('out_invoice', 'in_refund'): + if invoice_type in ('out_invoice', 'out_refund'): partner_value.update({'customer': True}) - if invoice_type in ('in_invoice', 'out_refund'): + if invoice_type in ('in_invoice', 'in_refund'): partner_value.update({'supplier': True}) - partner_id = company_pool.edi_import_as_partner(cr, uid, edi_document, values=partner_value, context=context) + partner_id = res_partner.write(cr, uid, [partner_id], partner_value, context=context) - # partner_id field is modified to point to the new partner - res = partner_pool.address_get(cr, uid, [partner_id], ['contact', 'invoice']) - address_id = res['invoice'] - partner = partner_pool.browse(cr, uid, partner_id, context=context) - partner_address = partner_address_pool.browse(cr, uid, address_id, context=context) - edi_document['partner_id'] = self.edi_m2o(cr, uid, partner, context=context) - edi_document['address_invoice_id'] = self.edi_m2o(cr, uid, partner_address, context=context) + # imported company_address = new partner address + address_info = edi_document['company_address'] + address_info['partner_id'] = (company_id, 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 del edi_document['company_id'] + del edi_document['company_address'] + partner_address = res_partner_address.browse(cr, uid, address_id, context=context) + edi_document['partner_id'] = (company_id, company_name) + 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 @@ -168,7 +145,7 @@ class account_invoice(osv.osv, ir_edi.edi): - 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' + - 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 @@ -176,21 +153,22 @@ class account_invoice(osv.osv, ir_edi.edi): 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. + on the tax config of the DB where it is imported. """ if context is None: context = {} - - #import company as a new partner - partner_id = self.edi_import_company(cr, uid, edi_document, context=context) + self._edi_requires_attributes(('company_id','company_address','type','invoice_line'), edi_document) # 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.get_invoice_account(cr, uid, partner_id, invoice_type, context=context) + 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' @@ -200,44 +178,34 @@ class account_invoice(osv.osv, ir_edi.edi): # internal number: reset to False, auto-generated edi_document['internal_number'] = False - # journal_id: should be selected based on type: simply put the 'type' in the context when calling create(), will be selected correctly - journal = self.get_invoice_journal(cr, uid, invoice_type, context=context) - edi_document['journal_id'] = journal and self.edi_m2o(cr, uid, journal, context=context) or False + 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.get('invoice_line', []): - product_id = edi_invoice_line.get('product_id', False) - account = False - if product_id: - product_name = product_id and product_id[1] - product_id = self.edi_import_relation(cr, uid, 'product.product', product_name, context=context) - account = self.get_product_account(cr, uid, product_id, invoice_type, context=context) - # TODO: add effect of fiscal position + 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'] = account and self.edi_m2o(cr, uid, account, context=context) or False + edi_invoice_line['account_id'] = self.edi_m2o(cr, uid, account, context=context) if account else False # 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', []): - tax_account = self.get_tax_account(cr, uid, context=context) - if tax_account: - edi_tax_line['account_id'] = self.edi_m2o(cr, uid, tax_account, context=context) + edi_tax_line['account_id'] = tax_account_info edi_tax_line['manual'] = True - # TODO :=> payment_term: if set, create a default one based on name... return super(account_invoice,self).edi_import(cr, uid, edi_document, context=context) - -account_invoice() -class account_invoice_line(osv.osv, ir_edi.edi): +class account_invoice_line(osv.osv, EDIMixin): _inherit='account.invoice.line' -account_invoice_line() -class account_invoice_tax(osv.osv, ir_edi.edi): +class account_invoice_tax(osv.osv, EDIMixin): _inherit = "account.invoice.tax" -account_invoice_tax() - diff --git a/addons/account/edi_invoice_action_data.xml b/addons/account/edi/invoice_action_data.xml similarity index 53% rename from addons/account/edi_invoice_action_data.xml rename to addons/account/edi/invoice_action_data.xml index 6a2338fadbf..5491e5f6e9b 100644 --- a/addons/account/edi_invoice_action_data.xml +++ b/addons/account/edi/invoice_action_data.xml @@ -3,7 +3,7 @@ - + ") @@ -12,11 +12,11 @@ try: tmpl = self.pool.get('ir.model.data').get_object(cr, uid, 'account', 'email_template_edi_invoice')[1] edi_token = self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0] - context.update(edi_web_invoice_url_view='%s/web/view_edi?db=%s&token=%s' %(web_root_url,cr.dbname, edi_token)) + context.update(edi_web_invoice_url_view='%s/web/view_edi?db=%s&token=%s' %(web_root_url, cr.dbname, edi_token)) self.pool.get('email.template').send_mail(cr, uid,tmpl.id,object.id,context=context) except: pass - + ]]> code ir.actions.server @@ -41,75 +41,75 @@ except: ${object.company_id.name} Invoice (Ref ${object.number or 'n/a' }) ${object.address_invoice_id.email or ''} - -<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> +

Hello${object.address_invoice_id.name and ' ' or ''}${object.address_invoice_id.name or ''},

- <p>A new invoice is available for ${object.partner_id.name}: </p> +

A new invoice is available for ${object.partner_id.name}:

- <p style="border-left: 1px solid #8e0000; margin-left: 30px;"> -   <strong>REFERENCES</strong><br /> -   Invoice number: <strong>${object.number}</strong><br /> -   Invoice amount: <strong>${object.amount_total} ${object.currency_id.name}</strong><br /> -   Invoice date: ${object.date_invoice or 'n/a'}<br /> -   Order reference: ${object.origin or 'n/a'}<br /> -   Your contact: <a href="mailto:${object.user_id.user_email or ''}?subject=Invoice%20${object.number}">${object.user_id.name}</a> - </p> +

+   REFERENCES
+   Invoice number: ${object.number}
+   Invoice amount: ${object.amount_total} ${object.currency_id.name}
+   Invoice date: ${object.date_invoice or 'n/a'}
+   Order reference: ${object.origin or 'n/a'}
+   Your contact: ${object.user_id.name} +

- <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-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(142, 0, 0); border-radius: 2px 2px; background-position: 0% 0%; background-repeat: repeat no-repeat;" - href="${ctx.get('edi_web_invoice_url_view') or ''}">View Invoice</a> +

+ View Invoice % if object.company_id.paypal_account: - <br/> - <p>It is also possible to directly pay with Paypal:</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-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(142, 0, 0); border-radius: 2px 2px; background-position: 0% 0%; background-repeat: repeat no-repeat;" +
+

It is also possible to directly pay with Paypal:

+ Pay with Paypal % 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-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; overflow-x: hidden; overflow-y: hidden; zoom: 1; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(142, 0, 0); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-bottom-right-radius: 0px 0px; border-bottom-left-radius: 0px 0px; background-position: 0% 0%; background-repeat: repeat no-repeat; "> - <h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 9px; padding-right: 14px; padding-bottom: 9px; padding-left: 14px; font-size: 12px; font-weight: normal; font-style: normal; color: rgb(255, 255, 255); "> - <strong style="text-transform:uppercase;">${object.company_id.name}</strong></h3> - </div> - <div style="width: 347px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 12px; padding-right: 14px; padding-bottom: 12px; padding-left: 14px; overflow-x: hidden; overflow-y: hidden; zoom: 1; line-height: 16px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(242, 242, 242); "> - <span style="color: rgb(38, 72, 149); margin-bottom: 5px; display: block; "> +
+

If you have any question, do not hesitate to contact us.

+

Thank you for choosing ${object.company_id.name or 'us'}!

+
+
+
+

+ ${object.company_id.name}

+
+
+ % if object.company_id.street: - ${object.company_id.street}<br/> + ${object.company_id.street}
% endif % if object.company_id.street2: - ${object.company_id.street2}<br/> + ${object.company_id.street2}
% endif % if object.company_id.city or object.company_id.zip: - ${object.company_id.zip} ${object.company_id.city}<br/> + ${object.company_id.zip} ${object.company_id.city}
% 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/> + ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
% 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> - - +

+
+ + ]]>
+ + ]]>
diff --git a/addons/account/test/test_edi_invoice.yml b/addons/account/test/test_edi_invoice.yml index 7d7678f7f9f..62d57988c9e 100644 --- a/addons/account/test/test_edi_invoice.yml +++ b/addons/account/test/test_edi_invoice.yml @@ -1,38 +1,20 @@ - - I create a company for Customer + In order to test the EDI export features of Invoices - - !record {model: res.company, id: res_company_test11}: - name: Thomson pvt. ltd. - partner_id: 1 - rml_header: 1 - rml_header2: 1 - rml_header3: 1 - currency_id: 1 - + First I create a draft customer invoice - - I create a partner which is a my customer -- - !record {model: res.partner, id: res_partner_test20}: - name: Junjun wala - supplier: False - company_id: res_company_test11 - opt_out: True -- - I create one customer invoice -- - !record {model: account.invoice, id: customer_invoice_test}: + !record {model: account.invoice, id: invoice_edi_1}: journal_id: 1 - partner_id: res_partner_test20 + partner_id: base.res_partner_agrolait currency_id: base.EUR - address_invoice_id: base.res_partner_address_11 - company_id: res_company_test11 + 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 - partner_id: res_partner_test20 uos_id: 1 quantity: 1.0 price_unit: 10.0 @@ -40,117 +22,93 @@ account_id: account.a_pay invoice_line: - product_id: product.product_product_pc3 - partner_id: res_partner_test20 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 Open the Invoice + 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}: | - - invoices = self.browse(cr, uid, ref("customer_invoice_test")) - import netsvc - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'account.invoice',invoices.id,'invoice_open', cr) - -- - I Testing of EDI functionality. First I export customer invoice from my company than import that invoice into customer company -- - !python {model: ir.edi.document}: | - invoice_pool = self.pool.get('account.invoice') - invoice = invoice_pool.browse(cr, uid, ref("customer_invoice_test")) - - tokens = self.export_edi(cr, uid, [invoice]) - assert tokens, 'Token is not generated' -- - I import of EDI document of custmer invoice -- - !python {model: ir.edi.document}: | - invoice_pool = self.pool.get('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", - "__last_update": False, - "country_id": ["b22acf7a-ddcd-11e0-a4db-701a04e25543:base.be", "Belgium"], - "__id": "b22acf7a-ddcd-11e0-a4db-701a04e25543:base.main_address", + "country_id": ["base:b22acf7a-ddcd-11e0-a4db-701a04e25543.be", "Belgium"], "phone": "(+32).81.81.37.00", "street": "Chaussee de Namur 40" }, - "company_id": ["b22acf7a-ddcd-11e0-a4db-701a04e25543:account.res_company_test11", "Thomson pvt. ltd."], - "currency_id": ["b22acf7a-ddcd-11e0-a4db-701a04e25543:base.EUR", "EUR (\u20ac)"], - "address_invoice_id": ["b22acf7a-ddcd-11e0-a4db-701a04e25543:base.res_partner_address_11", "Sebastien LANGE, France, Alencon, 1 place de l'\u00c9glise"], - "partner_id": ["b22acf7a-ddcd-11e0-a4db-701a04e25543:account.res_partner_test20", "Junjun wala"], - "__attachments": [], - "__module": "account", - "amount_total": 1010.0, + "company_id": ["account:b22acf7a-ddcd-11e0-a4db-701a04e25543.res_company_test11", "Thomson pvt. ltd."], + "currency_id": ["base:b22acf7a-ddcd-11e0-a4db-701a04e25543.EUR", "EUR (€)"], + "address_invoice_id": ["base:b22acf7a-ddcd-11e0-a4db-701a04e25543.res_partner_address_11", "Sebastien LANGE, France, Alencon, 1 place de l'\u00c9glise"], + "partner_id": ["account:b22acf7a-ddcd-11e0-a4db-701a04e25543.res_partner_test20", "Junjun wala"], "date_invoice": "2011-06-22", - "amount_untaxed": 10.0, - "name": "selling product", - "__model": "account.invoice", - "__last_update": False, + "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, - "__id": "b22acf7a-ddcd-11e0-a4db-701a04e25543:b22acf7a-ddcd-11e0-a4db-701a04e25543:account.account_invoice_tax-4g4EutbiEMVl", "name": "sale tax", - "__last_update": False }], - "__id": "b22acf7a-ddcd-11e0-a4db-701a04e25543:account.customer_invoice_test", - "amount_tax": 1000.0, - "__version": [6, 1], "type": "out_invoice", "invoice_line": [{ - "uos_id": ["b22acf7a-ddcd-11e0-a4db-701a04e25543:product.product_uom_unit", "PCE"], - "name": "basic pc", - "__last_update": False, + "__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, - "price_subtotal": 10.0, - "__id": "b22acf7a-ddcd-11e0-a4db-701a04e25543:b22acf7a-ddcd-11e0-a4db-701a04e25543:account.account_invoice_line-1RP3so-u2vV4", - "product_id": ["b22acf7a-ddcd-11e0-a4db-701a04e25543:product.product_product_pc1", "[PC1] Basic PC"], + "product_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_product_pc1", "[PC1] Basic PC"], "quantity": 1.0 }, { - "uos_id": ["b22acf7a-ddcd-11e0-a4db-701a04e25543:product.product_uom_unit", "PCE"], - "name": "Medim PC", - "__last_update": False, + "__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, - "price_subtotal": 100.0, - "__id": "b22acf7a-ddcd-11e0-a4db-701a04e25543:b22acf7a-ddcd-11e0-a4db-701a04e25543:account.account_invoice_line-1RP3so-u2vV4", - "product_id": ["b22acf7a-ddcd-11e0-a4db-701a04e25543:product.product_product_pc3", "[PC3] Medium PC"], + "product_id": ["product:b22acf7a-ddcd-11e0-a4db-701a04e25543.product_product_pc3", "[PC3] Medium PC"], "quantity": 5.0 }] } - - invoice_id = invoice_pool.edi_import(cr, uid, edi_document, context=context) - invoice_new = invoice_pool.browse(cr, uid, invoice_id, context=context) - assert invoice_id, 'Invoice is not imported' -- - I Checking the out invoice become in invoice or not after import -- - !python {model: account.invoice}: | - - new_partner_id = self.pool.get('res.partner').name_search(cr, uid, "Thomson pvt. ltd.") - assert new_partner_id, 'Partner is not created of Supplier' - ids = self.search(cr, uid, [('partner_id','=',new_partner_id[0][0]),('reference','=',"SAJ/2011/002")]) - assert ids, 'Invoice does not have created of party' - invoice_new = self.browse(cr, uid, ids[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) + 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.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', "Imported in voice is not supplier invoice" + 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': @@ -162,7 +120,7 @@ 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 == 100, "price sub total is not same" + assert inv_line.price_subtotal == 500, "price sub total is not same" else: assert 'wrong product imported in invoice lines' for inv_tax in invoice_new.tax_line: From 49ee09b8cee886942912b5dd1ad6e04492def6c1 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 5 Oct 2011 02:35:04 +0200 Subject: [PATCH 050/132] [REM] sale,purchase: temporarily disable EDI until reviewed bzr revid: odo@openerp.com-20111005003504-nqo2wm2313dfxvc3 --- addons/purchase/__init__.py | 2 +- addons/purchase/__openerp__.py | 4 ++-- addons/sale/__init__.py | 2 +- addons/sale/__openerp__.py | 4 ++-- addons/sale/edi_sale_order.py | 21 +++++++-------------- 5 files changed, 13 insertions(+), 20 deletions(-) diff --git a/addons/purchase/__init__.py b/addons/purchase/__init__.py index ddd572d8e81..623eae1d6a9 100644 --- a/addons/purchase/__init__.py +++ b/addons/purchase/__init__.py @@ -26,6 +26,6 @@ import wizard import report import stock import company -import edi_purchase_order +#import edi_purchase_order # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/purchase/__openerp__.py b/addons/purchase/__openerp__.py index 8b5c52d8333..d8415fc17c2 100644 --- a/addons/purchase/__openerp__.py +++ b/addons/purchase/__openerp__.py @@ -58,7 +58,7 @@ Dashboard for purchase management that includes: 'process/purchase_process.xml', 'report/purchase_report_view.xml', 'board_purchase_view.xml', - 'edi_purchase_order_data.xml', + #'edi_purchase_order_data.xml', ], 'test': [ 'test/purchase_from_order.yml', @@ -67,7 +67,7 @@ Dashboard for purchase management that includes: 'purchase_unit_test.xml', 'test/procurement_buy.yml', 'test/purchase_report.yml', - 'test/edi_purchase_order.yml', + #'test/edi_purchase_order.yml', ], 'demo': ['purchase_demo.xml'], 'installable': True, diff --git a/addons/sale/__init__.py b/addons/sale/__init__.py index 6361fea3a0b..379c2bec5b8 100644 --- a/addons/sale/__init__.py +++ b/addons/sale/__init__.py @@ -29,5 +29,5 @@ import sale_installer import wizard import report import company -import edi_sale_order +#import edi_sale_order # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale/__openerp__.py b/addons/sale/__openerp__.py index c862a4cbbfa..ae71e2c0bce 100644 --- a/addons/sale/__openerp__.py +++ b/addons/sale/__openerp__.py @@ -85,11 +85,11 @@ Dashboard for Sales Manager that includes: 'stock_view.xml', 'board_sale_view.xml', 'process/sale_process.xml', - 'edi_sale_order_data.xml', + #'edi_sale_order_data.xml', ], 'demo_xml': ['sale_demo.xml'], 'test': [ - 'test/edi_sale_order.yml', + #'test/edi_sale_order.yml', 'test/data_test.yml', 'test/manual_order_policy.yml', 'test/prepaid_order_policy.yml', diff --git a/addons/sale/edi_sale_order.py b/addons/sale/edi_sale_order.py index 69165de9b85..7cec8f39374 100644 --- a/addons/sale/edi_sale_order.py +++ b/addons/sale/edi_sale_order.py @@ -67,24 +67,17 @@ class sale_order(osv.osv, ir_edi.edi): #PO: No 'notes': True, } - } - company_pool = self.pool.get('res.company') + res_company = self.pool.get('res.company') edi_doc_list = [] for order in records: # Get EDI doc based on struct. The result will also contain all metadata fields and attachments. - edi_doc = super(sale_order,self).edi_export(cr, uid, [order], edi_struct, context) - if not edi_doc: - continue - edi_doc = edi_doc[0] + edi_doc = super(sale_order,self).edi_export(cr, uid, [order], edi_struct, context)[0] currency = order.company_id.currency_id - # Add company info and address - edi_company_document = company_pool.edi_export_address(cr, uid, [order.company_id], context=context)[order.company_id.id] - edi_doc.update({ - 'company_address': edi_company_document['company_address'], - 'currency_id': currency and self.edi_m2o(cr, uid, currency, context=context) or False, - #'company_logo': edi_company_document['company_logo'],#TODO - }) + edi_address = res_company.edi_export_address(cr, uid, [order.company_id], context=context)[0] + edi_doc.update(company_address=edi_address, + currency_id=(currency and self.edi_m2o(cr, uid, currency, context=context) or False)) + #TODO: company_logo edi_doc_list.append(edi_doc) return edi_doc_list @@ -124,7 +117,7 @@ class sale_order(osv.osv, ir_edi.edi): def edi_import(self, cr, uid, edi_document, context=None): if context is None: context = {} - + #import company as a new partner partner_id = self.edi_import_company(cr, uid, edi_document, context=context) From 0f13e3052c2431505064d390b86a670160304ce1 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 5 Oct 2011 02:44:33 +0200 Subject: [PATCH 051/132] [ADD] edi: moved web addon into edi module (needs cleanup) bzr revid: odo@openerp.com-20111005004433-28v6rcvl0xfqn48w --- addons/edi/__init__.py | 5 +- addons/edi/__openerp__.py | 3 + addons/edi/controllers/__init__.py | 1 + addons/edi/controllers/main.py | 88 ++++++++ addons/edi/static/src/js/edi.js | 345 +++++++++++++++++++++++++++++ addons/edi/static/src/xml/edi.xml | 103 +++++++++ 6 files changed, 544 insertions(+), 1 deletion(-) create mode 100644 addons/edi/controllers/__init__.py create mode 100644 addons/edi/controllers/main.py create mode 100644 addons/edi/static/src/js/edi.js create mode 100644 addons/edi/static/src/xml/edi.xml diff --git a/addons/edi/__init__.py b/addons/edi/__init__.py index 4a7808548d2..3aa7f74ebd7 100644 --- a/addons/edi/__init__.py +++ b/addons/edi/__init__.py @@ -21,4 +21,7 @@ import models import edi_service -from models.edi import EDIMixin, edi_document \ No newline at end of file +from models.edi import EDIMixin, edi_document + +# web +import controllers \ No newline at end of file diff --git a/addons/edi/__openerp__.py b/addons/edi/__openerp__.py index 414dff0e52c..80aa8de2f9f 100644 --- a/addons/edi/__openerp__.py +++ b/addons/edi/__openerp__.py @@ -43,6 +43,9 @@ technical OpenERP documentation at http://doc.openerp.com 'test': [ 'test/edi_partner_test.yml', ], + 'js': [ + 'static/src/js/edi.js' + ], 'installable': True, 'active': False, #'certificate': TODO diff --git a/addons/edi/controllers/__init__.py b/addons/edi/controllers/__init__.py new file mode 100644 index 00000000000..039d9715fab --- /dev/null +++ b/addons/edi/controllers/__init__.py @@ -0,0 +1 @@ +import main \ No newline at end of file diff --git a/addons/edi/controllers/main.py b/addons/edi/controllers/main.py new file mode 100644 index 00000000000..6f4a8d9657e --- /dev/null +++ b/addons/edi/controllers/main.py @@ -0,0 +1,88 @@ +#from base.controllers.main import View +import web.common as openerpweb +import json +import cherrypy + +class EDIView(openerpweb.Controller): + # http://path.to.web.client:8080/edi/view_edi?db=XXX&token=XXXXXXXXXXX + _cp_path = "/edi/view_edi" + + @openerpweb.httprequest + def index(self, req, token, db): + js_list = [] + css_list = [] + for addon in openerpweb.addons_module: + if addon in ['web_edi']: + continue + manifest = openerpweb.addons_manifest.get(addon, {}) + edi_addon = manifest.get('edi', False) + + if edi_addon: + js_list += map(lambda x: '/'+addon+'/'+x, manifest.get('js', [])) + css_list += map(lambda x: '/'+addon+'/'+x, manifest.get('css', [])) + + out = """ + + + + + OpenERP + + + + + + + + + + """ + for js in js_list: + out += """""" %(js) + for css in css_list: + out += """""" %(css) + out += """ + + + + + + """ + return out + +class EDIGet(openerpweb.Controller): + # http://path.to.web.client:8080/edi/get_edi?db=XXXX&token=XXXXXXXXXXX + _cp_path = "/edi/get_edi" + + @openerpweb.httprequest + def index(self, req, token, db): + response = req.session.proxy('edi').get_edi_document(token, db) + cherrypy.response.headers['Content-Type'] = 'application/json' + cherrypy.response.headers['Content-Length'] = len(response) + return response + + @openerpweb.jsonrequest + def get_edi_document(self, req, token, db): + response = req.session.proxy('edi').get_edi_document(token, db) + document = json.loads(response) + model = document and document[0].get('__model') + cherrypy.response.headers['Content-Type'] = 'application/json' + cherrypy.response.headers['Content-Length'] = len(response) + return {'token': token, 'db': db, 'model':model.replace('.','_'), 'document':response} + +class EDIImport(openerpweb.Controller): + #http://localhost:8002/base/static/src/base.html?import_edi&edi_url=XXXXX + # Note: edi_url should have url which passed from encodeURIComponent function + _cp_path = "/edi/import_edi" + + @openerpweb.jsonrequest + def index(self, req, edi_url): + # call EDI Service: import_edi_url + print 'EDI_IMPORT', req, edi_url + res = req.session.proxy('edi').import_edi_url(req.session._db, req.session._uid, req.session._password, edi_url) + return res + diff --git a/addons/edi/static/src/js/edi.js b/addons/edi/static/src/js/edi.js new file mode 100644 index 00000000000..be19feff197 --- /dev/null +++ b/addons/edi/static/src/js/edi.js @@ -0,0 +1,345 @@ +/*--------------------------------------------------------- + * OpenERP Web_edi + *---------------------------------------------------------*/ +// +openerp.edi = {} + +openerp.edi.EdiView = Class.extend({ + element_id: "oe", + template: "EdiView", + init: function(element_id){ + var self = this; + this.element_id = element_id; + this.$element = $('#' + element_id); + QWeb.add_template("/web_edi/static/src/xml/edi.xml"); + + }, + start: function() { + }, + view_edi: function(token, db) { + var params = {}; + if(token) params['token'] = token; + if(db) params['db'] = db; + this.rpc('/edi/get_edi/get_edi_document', params, this.on_response); + }, + on_response: function(value){ + this.element_id = "oe"; + this.template = "EdiView"; + this.$element = $('#' + this.element_id); + this.$element.html(QWeb.render(this.template, this)); + this.registry = openerp.edi.forms; + var model = value.model + var edi_document = value.document + + // EDI Center Frame + key_edi_center = 'edi_center' + edi_center = this.registry.get_object(key_edi_center + '_' + model) + if (edi_center == undefined){ + edi_center = this.registry.get_object(key_edi_center) + } + $edi_center_frame = this.$element.find('div.oe_edi_center'); + var center_frame = new (edi_center)($edi_center_frame, value); + center_frame.render(); + + // EDI Right TOP Frame + key_edi_right_top = 'edi_right_top' + edi_right_top = this.registry.get_object(key_edi_right_top + '_' + model) + if (edi_right_top == undefined){ + edi_right_top = this.registry.get_object(key_edi_right_top) + } + $edi_right_top_frame = this.$element.find('div.oe_edi_right_top'); + var right_top_frame = new (edi_right_top)($edi_right_top_frame, value); + right_top_frame.render(); + + //EDI Right Bottom Frame + key_edi_right_bottom = 'edi_right_bottom' + edi_right_bottom = this.registry.get_object(key_edi_right_bottom + '_' + model) + if (edi_right_bottom == undefined){ + edi_right_bottom = this.registry.get_object(key_edi_right_bottom) + } + $edi_right_bottom_frame = this.$element.find('div.oe_edi_right_bottom'); + var right_bottom_frame = new (edi_right_bottom)($edi_right_bottom_frame, value); + right_bottom_frame.render(); + }, + rpc: function(url, params, success_callback, error_callback) { + var self = this; + // Call using the rpc_mode + var deferred = $.Deferred(); + this.rpc_ajax(url, { + jsonrpc: "2.0", + method: "call", + params: params, + id:null + }).then(function () {deferred.resolve.apply(deferred, arguments);}, + function(error) {deferred.reject(error, $.Event());}); + return deferred.fail(function() { + deferred.fail(function(error, event) { + if (!event.isDefaultPrevented()) { + self.on_rpc_error(error, event); + } + }); + }).then(success_callback, error_callback).promise(); + }, + /** + * Raw JSON-RPC call + * + * @returns {jQuery.Deferred} ajax-based deferred object + */ + rpc_ajax: function(url, payload) { + var self = this; + this.on_rpc_request(); + // url can be an $.ajax option object + if (_.isString(url)) { + url = { + url: url + } + } + var ajax = _.extend({ + type: "POST", + url: url, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload), + processData: false + }, url); + var deferred = $.Deferred(); + $.ajax(ajax).done(function(response, textStatus, jqXHR) { + self.on_rpc_response(); + if (response.error) { + if (response.error.data.type == "session_invalid") { + self.uid = false; + self.on_session_invalid(function() { + self.rpc(url, payload.params, + function() {deferred.resolve.apply(deferred, arguments);}, + function(error, event) {event.preventDefault(); + deferred.reject.apply(deferred, arguments);}); + }); + } else { + deferred.reject(response.error); + } + } else { + deferred.resolve(response["result"], textStatus, jqXHR); + } + }).fail(function(jqXHR, textStatus, errorThrown) { + self.on_rpc_response(); + var error = { + code: -32098, + message: "XmlHttpRequestError " + errorThrown, + data: {type: "xhr"+textStatus, debug: jqXHR.responseText, objects: [jqXHR, errorThrown] } + }; + deferred.reject(error); + }); + return deferred.promise(); + }, + on_rpc_request: function() { + }, + on_rpc_response: function() { + }, + on_rpc_error: function(error) { + }, +}); + + + +openerp.edi.EdiViewCenter = Class.extend({ + init: function(element, edi){ + var self = this; + this.edi_document = edi.document; + this.$element = element; + QWeb.add_template("/web_edi/static/src/xml/edi.xml"); + }, + start: function() { + }, + render: function(){ + template = "EdiViewCenter"; + this.$element.append($(QWeb.render(template, {'view': this}))) + } +}); + +openerp.edi.EdiViewRightTop = Class.extend({ + init: function(element, edi){ + var self = this; + this.edi_document = edi.document; + this.$element = element; + QWeb.add_template("/web_edi/static/src/xml/edi.xml"); + this.$_element = $('
') + .appendTo(document.body) + .delegate('button.oe_edi_button_import', 'click', {'edi': edi} , this.do_import) + }, + start: function() { + }, + render: function(){ + template = "EdiViewRightTop"; + if (this.$current) { + this.$current.remove(); + } + this.$current = this.$_element.clone(true); + this.$current.empty().append(QWeb.render(template, this)); + this.$element.append(this.$current); + + }, + do_import: function(e){ + $element = $(e.view.document.body) + token = e.data.edi.token + db = e.data.edi.db + var current_url = $(location).attr('href') + var pathName = current_url.substring(0, current_url.lastIndexOf('/') +1); + + if ($element.find('#oe_edi_rd_import_openerp').attr('checked') == true) + { + // import EDI + server_url = $element.find('#oe_edi_txt_server_url').val() + edi_url = pathName + 'get_edi?db=' + db + '&token=' + token + edi_url = encodeURIComponent(edi_url) + window.location = 'http://' + server_url + '/base/static/src/base.html?import_edi&edi_url=' + edi_url + } + if ($element.find('#oe_edi_rd_import_saas_account').attr('checked') == true) + { + // create SAAS Account + } + if ($element.find('#oe_edi_rd_import_other').attr('checked') == true) + { + // GET EDI document + edi_url = pathName + 'get_edi?db=' + db + '&token=' + token + window.location = edi_url + } + } +}); + +openerp.edi.EdiViewRightBottom = Class.extend({ + init: function(element, edi){ + var self = this; + this.edi_document = edi.document; + this.$element = element; + QWeb.add_template("/web_edi/static/src/xml/edi.xml"); + }, + start: function() { + }, + render: function(){ + template = "EdiViewRightBottom"; + this.$element.append($(QWeb.render(template, this))) + } +}); + +openerp.edi.Registry = Class.extend({ + init: function (mapping) { + this.map = mapping || {}; + }, + + get_object: function (key) { + var path_string = this.map[key]; + if (path_string === undefined) { + // key is not Found + return undefined; + } + + var object_match = openerp; + var path = path_string.split('.'); + // ignore first section + for(var i=1; iImport successful, click Ok to see the new document
').dialog({ + modal: true, + title: 'Successful', + buttons: { + Ok: function() { + $(this).dialog("close"); + var action = { + "res_model": response[0][0], + "res_id": parseInt(response[0][1], 10), + "views":[[false,"form"]], + "type":"ir.actions.act_window", + "view_type":"form", + "view_mode":"form" + } + action.flags = { + search_view: false, + sidebar : false, + views_switcher : false, + action_buttons : false, + pager: false + } + var action_manager = new openerp.base.ActionManager(self.session, 'oe_app'); + + action_manager.start(); + action_manager.do_action(action); + } + } + + }); + } + else{ + $(QWeb.render("DialogWarning", "Sorry, Import is not successful.")).dialog({ + modal: true, + buttons: { + Ok: function() { + $(this).dialog("close"); + } + } + }); + } + + }); + } +}); +var parameters = jQuery.deparam(jQuery.param.querystring()); + + +if('import_edi' in parameters) { + var import_edi = new openerp.web_edi.EdiImport(); + import_edi.import_edi(parameters.edi_url); +} +} +// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/edi/static/src/xml/edi.xml b/addons/edi/static/src/xml/edi.xml new file mode 100644 index 00000000000..9da758f6900 --- /dev/null +++ b/addons/edi/static/src/xml/edi.xml @@ -0,0 +1,103 @@ + From c018cd9bf34f2c9cedbb7a5af2fed70045c86529 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 5 Oct 2011 03:04:01 +0200 Subject: [PATCH 052/132] [ADD] account: moved web addon into account module (needs review) bzr revid: odo@openerp.com-20111005010401-x6t8bdo7th8t2won --- addons/account/__openerp__.py | 3 + addons/account/static/src/js/edi_invoice.js | 50 +++++++ addons/account/static/src/xml/edi_invoice.xml | 130 ++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 addons/account/static/src/js/edi_invoice.js create mode 100644 addons/account/static/src/xml/edi_invoice.xml diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index 77f0f47569a..0bed25f87bc 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -150,6 +150,9 @@ module named account_voucher. 'test/test_edi_invoice.yml', 'test/account_report.yml', ], + "js": [ + 'static/src/js/edi_invoice.js' + ], 'installable': True, 'active': False, 'certificate': '0080331923549', diff --git a/addons/account/static/src/js/edi_invoice.js b/addons/account/static/src/js/edi_invoice.js new file mode 100644 index 00000000000..c31c803d665 --- /dev/null +++ b/addons/account/static/src/js/edi_invoice.js @@ -0,0 +1,50 @@ +/*--------------------------------------------------------- + * OpenERP Web_edi + *---------------------------------------------------------*/ +// +openerp.edi.InvoiceEdiView = Class.extend({ + init: function(element, edi){ + var self = this; + this.edi_document = eval(edi.document) + this.$element = element; + QWeb.add_template("/web_edi_invoice/static/src/xml/edi_invoice.xml"); + this.$_element = $('
') + .appendTo(document.body) + .delegate('#oe_edi_invoice_button_pay', 'click', {'edi': edi} , this.do_pay) + }, + start: function() { + }, + render: function(){ + template = "InvoiceEdiView"; + if (this.$current) { + this.$current.remove(); + } + this.$current = this.$_element.clone(true); + this.$current.empty().append($(QWeb.render(template, {'invoices': this.edi_document}))); + this.$element.append(this.$current); + }, + do_pay: function(e){ + $element = $(e.view.document.body) + token = e.data.edi.token + db = e.data.edi.db + var current_url = $(location).attr('href') + var pathName = current_url.substring(0, current_url.lastIndexOf('/') +1); + + if ($element.find('#oe_edi_invoice_rd_pay_paypal').attr('checked') == true) + { + alert('Pay Invoice using Paypal service'); + + } + if ($element.find('#oe_edi_invoice_rd_pay_google_checkout').attr('checked') == true) + { + alert('Pay Invoice using Google Checkout'); + } + if ($element.find('#oe_edi_invoice_rd_pay_bank').attr('checked') == true) + { + alert('Pay Invoice using Bankwire Trasnfer') + } + } +}); + +openerp.edi.forms.add('edi_center_account_invoice' , 'openerp.edi.InvoiceEdiView'); +// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/account/static/src/xml/edi_invoice.xml b/addons/account/static/src/xml/edi_invoice.xml new file mode 100644 index 00000000000..9f68c7fedf8 --- /dev/null +++ b/addons/account/static/src/xml/edi_invoice.xml @@ -0,0 +1,130 @@ + From 1221bdccf35186100259d79398e12410bd076bac Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 5 Oct 2011 03:08:39 +0200 Subject: [PATCH 053/132] [IMP] edi: updated to latest web addon changes (needs review) bzr revid: odo@openerp.com-20111005010839-nas8ddkp53pzgx4o --- addons/edi/__openerp__.py | 4 +- addons/edi/controllers/__init__.py | 4 +- addons/edi/controllers/edi_get.py | 26 ++ addons/edi/controllers/edi_import.py | 71 +++++ addons/edi/controllers/edi_view.py | 92 +++++++ addons/edi/controllers/main.py | 88 ------ addons/edi/static/src/js/edi.js | 345 ------------------------ addons/edi/static/src/js/edi_import.js | 75 ++++++ addons/edi/static/src/js/edi_view.js | 148 ++++++++++ addons/edi/static/src/js/sessionless.js | 75 ++++++ 10 files changed, 493 insertions(+), 435 deletions(-) create mode 100644 addons/edi/controllers/edi_get.py create mode 100644 addons/edi/controllers/edi_import.py create mode 100644 addons/edi/controllers/edi_view.py delete mode 100644 addons/edi/controllers/main.py delete mode 100644 addons/edi/static/src/js/edi.js create mode 100644 addons/edi/static/src/js/edi_import.js create mode 100644 addons/edi/static/src/js/edi_view.js create mode 100644 addons/edi/static/src/js/sessionless.js diff --git a/addons/edi/__openerp__.py b/addons/edi/__openerp__.py index 80aa8de2f9f..f3b11bbfadb 100644 --- a/addons/edi/__openerp__.py +++ b/addons/edi/__openerp__.py @@ -44,7 +44,9 @@ technical OpenERP documentation at http://doc.openerp.com 'test/edi_partner_test.yml', ], 'js': [ - 'static/src/js/edi.js' + 'static/src/js/sessionless.js', + 'static/src/js/edi_import.js', + 'static/src/js/edi_view.js', ], 'installable': True, 'active': False, diff --git a/addons/edi/controllers/__init__.py b/addons/edi/controllers/__init__.py index 039d9715fab..45a091bdacc 100644 --- a/addons/edi/controllers/__init__.py +++ b/addons/edi/controllers/__init__.py @@ -1 +1,3 @@ -import main \ No newline at end of file +import edi_get +import edi_view +import edi_import diff --git a/addons/edi/controllers/edi_get.py b/addons/edi/controllers/edi_get.py new file mode 100644 index 00000000000..1e8f61708c8 --- /dev/null +++ b/addons/edi/controllers/edi_get.py @@ -0,0 +1,26 @@ +import werkzeug.wrappers +import web.common.dispatch as openerpweb +import json + +class EDIGet(openerpweb.Controller): + # http://path.to.web.client:8080/web/get_edi?db=XXXX&token=XXXXXXXXXXX + _cp_path = "/web/get_edi" + + @openerpweb.httprequest + def index(self, req, token, db): + result = req.session.proxy('edi').get_edi_document(token, db) + response = werkzeug.wrappers.Response( + result, headers=[('Content-Type', 'text/html; charset=utf-8'), + ('Content-Length', len(result))]) + + return response + + @openerpweb.jsonrequest + def get_edi_document(self, req, token, db): + result = req.session.proxy('edi').get_edi_document(token, db) + document = json.loads(result) + model = document and document[0].get('__model') + return {'token': token, 'db': db, 'model':model.replace('.','_'), 'document':document} + + + diff --git a/addons/edi/controllers/edi_import.py b/addons/edi/controllers/edi_import.py new file mode 100644 index 00000000000..3f99f6cdfbb --- /dev/null +++ b/addons/edi/controllers/edi_import.py @@ -0,0 +1,71 @@ +import werkzeug.wrappers +import web.common.dispatch as openerpweb +import web.controllers.main as web +import json +import textwrap + + + +edi_import_template = textwrap.dedent(""" + + + + OpenERP + + + %(css)s + %(javascript)s + + + + + +""") + + +class EDIImport(web.WebClient): + #http://localhost:8002/web/import_edi?edi_url=XXXXX + # Note: edi_url should have url which passed from encodeURIComponent function + _cp_path = "/web/import_edi" + + + @openerpweb.httprequest + def index(self, req, edi_url): + # script tags + addons = ['web'] + jslist = ['/web/webclient/js'] + if req.debug: + jslist = [i + '?debug=' + str(time.time()) for i in web.manifest_glob(req.config.addons_path, addons, 'js')] + js = "\n ".join([''%i for i in jslist]) + + # css tags + csslist = ['/web/webclient/css'] + if req.debug: + csslist = [i + '?debug=' + str(time.time()) for i in web.manifest_glob(req.config.addons_path, addons, 'css')] + css = "\n ".join([''%i for i in csslist]) + + r = edi_import_template % { + 'javascript': js, + 'css': css, + 'edi_url': edi_url, + } + return r + + @openerpweb.jsonrequest + def import_edi_url(self, req, edi_url): + # call EDI Service: import_edi_url + result = req.session.proxy('edi').import_edi_url(req.session._db, req.session._uid, req.session._password, edi_url) + + return result + diff --git a/addons/edi/controllers/edi_view.py b/addons/edi/controllers/edi_view.py new file mode 100644 index 00000000000..e2e73c05119 --- /dev/null +++ b/addons/edi/controllers/edi_view.py @@ -0,0 +1,92 @@ +import werkzeug.wrappers +import web.common.dispatch as openerpweb +import web.controllers.main as web +import json +import textwrap + +edi_view_template = textwrap.dedent(""" + + + + OpenERP + + %(css)s + %(javascript)s + + + + +""") + +def edi_addons(): + _addons = ['web', 'web_edi'] + for addon in openerpweb.addons_module: + if addon in _addons: + continue + manifest = openerpweb.addons_manifest.get(addon, {}) + edi_addon = manifest.get('edi', False) + + if edi_addon: + _addons.append(addon) + return _addons + +class EDIView(openerpweb.Controller): + # http://path.to.web.client:8080/web/view_edi?db=XXX&token=XXXXXXXXXXX + _cp_path = "/web/view_edi" + + @openerpweb.httprequest + def css(self, req): + files = web.manifest_glob(req.config.addons_path, edi_addons(), 'css') + content,timestamp = web.concat_files(req.config.addons_path, files) + # TODO request set the Date of last modif and Etag + return req.make_response(content, [('Content-Type', 'text/css')]) + + @openerpweb.httprequest + def js(self, req): + files = web.manifest_glob(req.config.addons_path, edi_addons(), 'js') + content,timestamp = web.concat_files(req.config.addons_path, files) + # TODO request set the Date of last modif and Etag + return req.make_response(content, [('Content-Type', 'application/javascript')]) + + @openerpweb.httprequest + def index(self, req, token, db): + # script tags + addons = edi_addons() + jslist = ['/web/view_edi/js'] + if req.debug: + jslist = [i + '?debug=' + str(time.time()) for i in web.manifest_glob(req.config.addons_path, addons, 'js')] + js = "\n ".join([''%i for i in jslist]) + + # css tags + csslist = ['/web/view_edi/css'] + if req.debug: + csslist = [i + '?debug=' + str(time.time()) for i in web.manifest_glob(req.config.addons_path, addons, 'css')] + css = "\n ".join([''%i for i in csslist]) + + js_files = [js_file.split('/')[-1].split('.')[0] for js_file in web.manifest_glob(req.config.addons_path, edi_addons(), 'js')] + + r = edi_view_template % { + 'javascript': js, + 'css': css, + 'token': token, + 'db': db, + 'edi_js': js_files + } + return r + + + diff --git a/addons/edi/controllers/main.py b/addons/edi/controllers/main.py deleted file mode 100644 index 6f4a8d9657e..00000000000 --- a/addons/edi/controllers/main.py +++ /dev/null @@ -1,88 +0,0 @@ -#from base.controllers.main import View -import web.common as openerpweb -import json -import cherrypy - -class EDIView(openerpweb.Controller): - # http://path.to.web.client:8080/edi/view_edi?db=XXX&token=XXXXXXXXXXX - _cp_path = "/edi/view_edi" - - @openerpweb.httprequest - def index(self, req, token, db): - js_list = [] - css_list = [] - for addon in openerpweb.addons_module: - if addon in ['web_edi']: - continue - manifest = openerpweb.addons_manifest.get(addon, {}) - edi_addon = manifest.get('edi', False) - - if edi_addon: - js_list += map(lambda x: '/'+addon+'/'+x, manifest.get('js', [])) - css_list += map(lambda x: '/'+addon+'/'+x, manifest.get('css', [])) - - out = """ - - - - - OpenERP - - - - - - - - - - """ - for js in js_list: - out += """""" %(js) - for css in css_list: - out += """""" %(css) - out += """ - - - - - - """ - return out - -class EDIGet(openerpweb.Controller): - # http://path.to.web.client:8080/edi/get_edi?db=XXXX&token=XXXXXXXXXXX - _cp_path = "/edi/get_edi" - - @openerpweb.httprequest - def index(self, req, token, db): - response = req.session.proxy('edi').get_edi_document(token, db) - cherrypy.response.headers['Content-Type'] = 'application/json' - cherrypy.response.headers['Content-Length'] = len(response) - return response - - @openerpweb.jsonrequest - def get_edi_document(self, req, token, db): - response = req.session.proxy('edi').get_edi_document(token, db) - document = json.loads(response) - model = document and document[0].get('__model') - cherrypy.response.headers['Content-Type'] = 'application/json' - cherrypy.response.headers['Content-Length'] = len(response) - return {'token': token, 'db': db, 'model':model.replace('.','_'), 'document':response} - -class EDIImport(openerpweb.Controller): - #http://localhost:8002/base/static/src/base.html?import_edi&edi_url=XXXXX - # Note: edi_url should have url which passed from encodeURIComponent function - _cp_path = "/edi/import_edi" - - @openerpweb.jsonrequest - def index(self, req, edi_url): - # call EDI Service: import_edi_url - print 'EDI_IMPORT', req, edi_url - res = req.session.proxy('edi').import_edi_url(req.session._db, req.session._uid, req.session._password, edi_url) - return res - diff --git a/addons/edi/static/src/js/edi.js b/addons/edi/static/src/js/edi.js deleted file mode 100644 index be19feff197..00000000000 --- a/addons/edi/static/src/js/edi.js +++ /dev/null @@ -1,345 +0,0 @@ -/*--------------------------------------------------------- - * OpenERP Web_edi - *---------------------------------------------------------*/ -// -openerp.edi = {} - -openerp.edi.EdiView = Class.extend({ - element_id: "oe", - template: "EdiView", - init: function(element_id){ - var self = this; - this.element_id = element_id; - this.$element = $('#' + element_id); - QWeb.add_template("/web_edi/static/src/xml/edi.xml"); - - }, - start: function() { - }, - view_edi: function(token, db) { - var params = {}; - if(token) params['token'] = token; - if(db) params['db'] = db; - this.rpc('/edi/get_edi/get_edi_document', params, this.on_response); - }, - on_response: function(value){ - this.element_id = "oe"; - this.template = "EdiView"; - this.$element = $('#' + this.element_id); - this.$element.html(QWeb.render(this.template, this)); - this.registry = openerp.edi.forms; - var model = value.model - var edi_document = value.document - - // EDI Center Frame - key_edi_center = 'edi_center' - edi_center = this.registry.get_object(key_edi_center + '_' + model) - if (edi_center == undefined){ - edi_center = this.registry.get_object(key_edi_center) - } - $edi_center_frame = this.$element.find('div.oe_edi_center'); - var center_frame = new (edi_center)($edi_center_frame, value); - center_frame.render(); - - // EDI Right TOP Frame - key_edi_right_top = 'edi_right_top' - edi_right_top = this.registry.get_object(key_edi_right_top + '_' + model) - if (edi_right_top == undefined){ - edi_right_top = this.registry.get_object(key_edi_right_top) - } - $edi_right_top_frame = this.$element.find('div.oe_edi_right_top'); - var right_top_frame = new (edi_right_top)($edi_right_top_frame, value); - right_top_frame.render(); - - //EDI Right Bottom Frame - key_edi_right_bottom = 'edi_right_bottom' - edi_right_bottom = this.registry.get_object(key_edi_right_bottom + '_' + model) - if (edi_right_bottom == undefined){ - edi_right_bottom = this.registry.get_object(key_edi_right_bottom) - } - $edi_right_bottom_frame = this.$element.find('div.oe_edi_right_bottom'); - var right_bottom_frame = new (edi_right_bottom)($edi_right_bottom_frame, value); - right_bottom_frame.render(); - }, - rpc: function(url, params, success_callback, error_callback) { - var self = this; - // Call using the rpc_mode - var deferred = $.Deferred(); - this.rpc_ajax(url, { - jsonrpc: "2.0", - method: "call", - params: params, - id:null - }).then(function () {deferred.resolve.apply(deferred, arguments);}, - function(error) {deferred.reject(error, $.Event());}); - return deferred.fail(function() { - deferred.fail(function(error, event) { - if (!event.isDefaultPrevented()) { - self.on_rpc_error(error, event); - } - }); - }).then(success_callback, error_callback).promise(); - }, - /** - * Raw JSON-RPC call - * - * @returns {jQuery.Deferred} ajax-based deferred object - */ - rpc_ajax: function(url, payload) { - var self = this; - this.on_rpc_request(); - // url can be an $.ajax option object - if (_.isString(url)) { - url = { - url: url - } - } - var ajax = _.extend({ - type: "POST", - url: url, - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify(payload), - processData: false - }, url); - var deferred = $.Deferred(); - $.ajax(ajax).done(function(response, textStatus, jqXHR) { - self.on_rpc_response(); - if (response.error) { - if (response.error.data.type == "session_invalid") { - self.uid = false; - self.on_session_invalid(function() { - self.rpc(url, payload.params, - function() {deferred.resolve.apply(deferred, arguments);}, - function(error, event) {event.preventDefault(); - deferred.reject.apply(deferred, arguments);}); - }); - } else { - deferred.reject(response.error); - } - } else { - deferred.resolve(response["result"], textStatus, jqXHR); - } - }).fail(function(jqXHR, textStatus, errorThrown) { - self.on_rpc_response(); - var error = { - code: -32098, - message: "XmlHttpRequestError " + errorThrown, - data: {type: "xhr"+textStatus, debug: jqXHR.responseText, objects: [jqXHR, errorThrown] } - }; - deferred.reject(error); - }); - return deferred.promise(); - }, - on_rpc_request: function() { - }, - on_rpc_response: function() { - }, - on_rpc_error: function(error) { - }, -}); - - - -openerp.edi.EdiViewCenter = Class.extend({ - init: function(element, edi){ - var self = this; - this.edi_document = edi.document; - this.$element = element; - QWeb.add_template("/web_edi/static/src/xml/edi.xml"); - }, - start: function() { - }, - render: function(){ - template = "EdiViewCenter"; - this.$element.append($(QWeb.render(template, {'view': this}))) - } -}); - -openerp.edi.EdiViewRightTop = Class.extend({ - init: function(element, edi){ - var self = this; - this.edi_document = edi.document; - this.$element = element; - QWeb.add_template("/web_edi/static/src/xml/edi.xml"); - this.$_element = $('
') - .appendTo(document.body) - .delegate('button.oe_edi_button_import', 'click', {'edi': edi} , this.do_import) - }, - start: function() { - }, - render: function(){ - template = "EdiViewRightTop"; - if (this.$current) { - this.$current.remove(); - } - this.$current = this.$_element.clone(true); - this.$current.empty().append(QWeb.render(template, this)); - this.$element.append(this.$current); - - }, - do_import: function(e){ - $element = $(e.view.document.body) - token = e.data.edi.token - db = e.data.edi.db - var current_url = $(location).attr('href') - var pathName = current_url.substring(0, current_url.lastIndexOf('/') +1); - - if ($element.find('#oe_edi_rd_import_openerp').attr('checked') == true) - { - // import EDI - server_url = $element.find('#oe_edi_txt_server_url').val() - edi_url = pathName + 'get_edi?db=' + db + '&token=' + token - edi_url = encodeURIComponent(edi_url) - window.location = 'http://' + server_url + '/base/static/src/base.html?import_edi&edi_url=' + edi_url - } - if ($element.find('#oe_edi_rd_import_saas_account').attr('checked') == true) - { - // create SAAS Account - } - if ($element.find('#oe_edi_rd_import_other').attr('checked') == true) - { - // GET EDI document - edi_url = pathName + 'get_edi?db=' + db + '&token=' + token - window.location = edi_url - } - } -}); - -openerp.edi.EdiViewRightBottom = Class.extend({ - init: function(element, edi){ - var self = this; - this.edi_document = edi.document; - this.$element = element; - QWeb.add_template("/web_edi/static/src/xml/edi.xml"); - }, - start: function() { - }, - render: function(){ - template = "EdiViewRightBottom"; - this.$element.append($(QWeb.render(template, this))) - } -}); - -openerp.edi.Registry = Class.extend({ - init: function (mapping) { - this.map = mapping || {}; - }, - - get_object: function (key) { - var path_string = this.map[key]; - if (path_string === undefined) { - // key is not Found - return undefined; - } - - var object_match = openerp; - var path = path_string.split('.'); - // ignore first section - for(var i=1; iImport successful, click Ok to see the new document
').dialog({ - modal: true, - title: 'Successful', - buttons: { - Ok: function() { - $(this).dialog("close"); - var action = { - "res_model": response[0][0], - "res_id": parseInt(response[0][1], 10), - "views":[[false,"form"]], - "type":"ir.actions.act_window", - "view_type":"form", - "view_mode":"form" - } - action.flags = { - search_view: false, - sidebar : false, - views_switcher : false, - action_buttons : false, - pager: false - } - var action_manager = new openerp.base.ActionManager(self.session, 'oe_app'); - - action_manager.start(); - action_manager.do_action(action); - } - } - - }); - } - else{ - $(QWeb.render("DialogWarning", "Sorry, Import is not successful.")).dialog({ - modal: true, - buttons: { - Ok: function() { - $(this).dialog("close"); - } - } - }); - } - - }); - } -}); -var parameters = jQuery.deparam(jQuery.param.querystring()); - - -if('import_edi' in parameters) { - var import_edi = new openerp.web_edi.EdiImport(); - import_edi.import_edi(parameters.edi_url); -} -} -// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/edi/static/src/js/edi_import.js b/addons/edi/static/src/js/edi_import.js new file mode 100644 index 00000000000..5219676405f --- /dev/null +++ b/addons/edi/static/src/js/edi_import.js @@ -0,0 +1,75 @@ + +openerp.web.edi_import = function(openerp) { +var QWeb = new QWeb2.Engine(); +openerp.web.EdiImport = openerp.web.WebClient.extend({ + + init: function(element_id) { + this._super(element_id); + }, + start: function() { + this._super(); + }, + do_import: function(){ + var self = this; + self.rpc('/web/import_edi/import_edi_url', self.params, function(response){ + if (response.length) { + $('
Import successful, click Ok to see the new document
').dialog({ + modal: true, + title: 'Successful', + buttons: { + Ok: function() { + $(this).dialog("close"); + var action = { + "res_model": response[0][0], + "res_id": parseInt(response[0][1], 10), + "views":[[false,"form"]], + "type":"ir.actions.act_window", + "view_type":"form", + "view_mode":"form" + } + action.flags = { + search_view: false, + sidebar : false, + views_switcher : false, + action_buttons : false, + pager: false + } + var action_manager = new openerp.web.ActionManager(self); + action_manager.appendTo($("#oe_app")); + action_manager.start(); + action_manager.do_action(action); + } + } + + }); + } + else{ + $(QWeb.render("DialogWarning", "Sorry, Import is not successful.")).dialog({ + modal: true, + buttons: { + Ok: function() { + $(this).dialog("close"); + } + } + }); + } + }); + }, + import_edi: function(edi_url) { + var self = this; + this.params = {}; + + if(edi_url) this.params['edi_url'] = decodeURIComponent(edi_url); + + if (!this.session.db){ + this.start(); + this.session.on_session_valid.add_last(self.do_import); + + } + else{ + self.do_import(); + } + } +}); +} +// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/edi/static/src/js/edi_view.js b/addons/edi/static/src/js/edi_view.js new file mode 100644 index 00000000000..713e7d1fe9f --- /dev/null +++ b/addons/edi/static/src/js/edi_view.js @@ -0,0 +1,148 @@ + +openerp.web.edi_view = function(openerp) { + openerp.web.edi_views = new openerp.web.Registry(); + var QWeb = new QWeb2.Engine(); + // EDI View Engine + openerp.web.EdiView = openerp.web.Sessionless.extend({ + init: function(element_id){ + var self = this; + this.template = "EdiView"; + this.element_id = element_id; + this.$element = $('#' + element_id); + QWeb.add_template("/web_edi/static/src/xml/edi.xml"); + + }, + start: function() { + }, + view_edi: function(token, db) { + var params = {}; + if(token) params['token'] = token; + if(db) params['db'] = db; + this.rpc('/web/get_edi/get_edi_document', params, this.on_response); + }, + + on_response: function(value){ + var self = this; + this.template = "EdiView"; + this.element_id = "oe"; + self.$element = $('#' + this.element_id); + self.$element.html(QWeb.render(self.template, this)); + + var edi_document = value.document; + + var render = function(key, element_id, response){ + var registry = openerp.web.edi_views; + model = response.model + // get namespace of view of particular model from registry + try { + edi_view = registry.get_object(key + '_' + model); + }catch (e) { + edi_view = undefined; + } + // if not found, take default namespace of view + if (edi_view == undefined){ + edi_view = registry.get_object(key) + } + $edi_frame = self.$element.find(element_id); + + // create instance of QWeb of view namespace + edi_qweb = new (edi_view)($edi_frame, response) + edi_qweb.render(); + }; + + // EDI Center Frame + render('view_center', 'div.oe_edi_center', value) + + // EDI Right TOP Frame + render('view_right_top', 'div.oe_edi_right_top', value) + + //EDI Right Bottom Frame + render('view_right_bottom', 'div.oe_edi_right_bottom', value) + + }, + + }); + +openerp.web.edi_views.add('view_center' , 'openerp.web.EdiViewCenter'); +openerp.web.EdiViewCenter = openerp.web.Class.extend({ + init: function(element, edi){ + var self = this; + this.edi_document = edi.document; + this.$element = element; + QWeb.add_template("/web_edi/static/src/xml/edi.xml"); + }, + start: function() { + }, + render: function(){ + template = "EdiViewCenter"; + this.$element.append($(QWeb.render(template, {'view': this}))) + } +}); + +openerp.web.edi_views.add('view_right_top' , 'openerp.web.EdiViewRightTop'); +openerp.web.EdiViewRightTop = openerp.web.Class.extend({ + init: function(element, edi){ + var self = this; + this.edi_document = edi.document; + this.$element = element; + QWeb.add_template("/web_edi/static/src/xml/edi.xml"); + this.$_element = $('
') + .appendTo(document.body) + .delegate('button.oe_edi_button_import', 'click', {'edi': edi} , this.do_import) + }, + start: function() { + }, + render: function(){ + template = "EdiViewRightTop"; + if (this.$current) { + this.$current.remove(); + } + this.$current = this.$_element.clone(true); + this.$current.empty().append(QWeb.render(template, this)); + this.$element.append(this.$current); + }, + do_import: function(e){ + $element = $(e.view.document.body) + token = e.data.edi.token + db = e.data.edi.db + var current_url = $(location).attr('href') + var pathName = current_url.substring(0, current_url.lastIndexOf('/') +1); + + if ($element.find('#oe_edi_rd_import_openerp').attr('checked') == 'checked') + { + // import EDI + server_url = $element.find('#oe_edi_txt_server_url').val() + edi_url = pathName + 'get_edi?db=' + db + '&token=' + token + edi_url = encodeURIComponent(edi_url) + window.location = 'http://' + server_url + '/web/import_edi?edi_url=' + edi_url + } + if ($element.find('#oe_edi_rd_import_saas_account').attr('checked') == 'checked') + { + // create SAAS Account + } + if ($element.find('#oe_edi_rd_import_other').attr('checked') == 'checked') + { + // GET EDI document + edi_url = pathName + 'get_edi?db=' + db + '&token=' + token + window.location = edi_url + } + } +}); + +openerp.web.edi_views.add('view_right_bottom' , 'openerp.web.EdiViewRightBottom'); +openerp.web.EdiViewRightBottom = openerp.web.Class.extend({ + init: function(element, edi){ + var self = this; + this.edi_document = edi.document; + this.$element = element; + QWeb.add_template("/web_edi/static/src/xml/edi.xml"); + }, + start: function() { + }, + render: function(){ + template = "EdiViewRightBottom"; + this.$element.append($(QWeb.render(template, this))) + } +}); +} +// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/edi/static/src/js/sessionless.js b/addons/edi/static/src/js/sessionless.js new file mode 100644 index 00000000000..8890ab938cd --- /dev/null +++ b/addons/edi/static/src/js/sessionless.js @@ -0,0 +1,75 @@ +openerp.web.sessionless = function(openerp) { + var QWeb = new QWeb2.Engine(); + openerp.web.Sessionless = openerp.web.SessionAware.extend({ + rpc: function(url, params, success_callback, error_callback) { + var self = this; + // Call using the rpc_mode + var deferred = $.Deferred(); + this.rpc_ajax(url, { + jsonrpc: "2.0", + method: "call", + params: params, + id:null + }).then(function () {deferred.resolve.apply(deferred, arguments);}, + function(error) {deferred.reject(error, $.Event());}); + return deferred.fail(function() { + deferred.fail(function(error, event) { + if (!event.isDefaultPrevented()) { + self.on_rpc_error(error, event); + } + }); + }).then(success_callback, error_callback).promise(); + }, + /** + * Raw JSON-RPC call + * + * @returns {jQuery.Deferred} ajax-webd deferred object + */ + rpc_ajax: function(url, payload) { + var self = this; + this.on_rpc_request(); + // url can be an $.ajax option object + if (_.isString(url)) { + url = { + url: url + } + } + var ajax = _.extend({ + type: "POST", + url: url, + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify(payload), + processData: false + }, url); + var deferred = $.Deferred(); + $.ajax(ajax).done(function(response, textStatus, jqXHR) { + self.on_rpc_response(); + if (!response.error) { + deferred.resolve(response["result"], textStatus, jqXHR); + return; + } + if (response.error.data.type !== "session_invalid") { + deferred.reject(response.error); + return; + } + + }).fail(function(jqXHR, textStatus, errorThrown) { + self.on_rpc_response(); + var error = { + code: -32098, + message: "XmlHttpRequestError " + errorThrown, + data: {type: "xhr"+textStatus, debug: jqXHR.responseText, objects: [jqXHR, errorThrown] } + }; + deferred.reject(error); + }); + return deferred.promise(); + }, + on_rpc_request: function() { + }, + on_rpc_response: function() { + }, + on_rpc_error: function(error) { + }, + }); +} From 770af0d0c21b2bf537ffb0f988f3004989187b9a Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 5 Oct 2011 03:09:36 +0200 Subject: [PATCH 054/132] [IMP] account: updated to latest edi web addon changes (needs review) bzr revid: odo@openerp.com-20111005010936-te7dn7ced0jceh19 --- addons/account/static/src/js/edi_invoice.js | 17 +++++++++++------ addons/account/static/src/xml/edi_invoice.xml | 10 ++++++---- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/addons/account/static/src/js/edi_invoice.js b/addons/account/static/src/js/edi_invoice.js index c31c803d665..9c1748d5bf9 100644 --- a/addons/account/static/src/js/edi_invoice.js +++ b/addons/account/static/src/js/edi_invoice.js @@ -1,8 +1,13 @@ /*--------------------------------------------------------- - * OpenERP Web_edi + * EDI Web Interface for Invoice *---------------------------------------------------------*/ // -openerp.edi.InvoiceEdiView = Class.extend({ + +openerp.web.edi_invoice = function(openerp) { +var QWeb = new QWeb2.Engine(); + +openerp.web.edi_views.add('view_center_account_invoice' , 'openerp.web.EdiViewCenterInvoice'); +openerp.web.EdiViewCenterInvoice = openerp.web.Class.extend({ init: function(element, edi){ var self = this; this.edi_document = eval(edi.document) @@ -30,21 +35,21 @@ openerp.edi.InvoiceEdiView = Class.extend({ var current_url = $(location).attr('href') var pathName = current_url.substring(0, current_url.lastIndexOf('/') +1); - if ($element.find('#oe_edi_invoice_rd_pay_paypal').attr('checked') == true) + if ($element.find('#oe_edi_invoice_rd_pay_paypal').attr('checked') == 'checked') { alert('Pay Invoice using Paypal service'); } - if ($element.find('#oe_edi_invoice_rd_pay_google_checkout').attr('checked') == true) + if ($element.find('#oe_edi_invoice_rd_pay_google_checkout').attr('checked') == 'checked') { alert('Pay Invoice using Google Checkout'); } - if ($element.find('#oe_edi_invoice_rd_pay_bank').attr('checked') == true) + if ($element.find('#oe_edi_invoice_rd_pay_bank').attr('checked') == 'checked') { alert('Pay Invoice using Bankwire Trasnfer') } } }); -openerp.edi.forms.add('edi_center_account_invoice' , 'openerp.edi.InvoiceEdiView'); +} // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/account/static/src/xml/edi_invoice.xml b/addons/account/static/src/xml/edi_invoice.xml index 9f68c7fedf8..2da11bf6475 100644 --- a/addons/account/static/src/xml/edi_invoice.xml +++ b/addons/account/static/src/xml/edi_invoice.xml @@ -29,10 +29,12 @@

Invoice :


-
-
-
- Phone: + +
+
+
+ Phone: +


From 61788499c2cb1ba748298eac6d8af7e058f400ed Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 5 Oct 2011 03:13:52 +0200 Subject: [PATCH 055/132] [ADD] sale: moved edi web addon into sale (needs review+refactoring for purchase) bzr revid: odo@openerp.com-20111005011352-bt8fatjg7x1leyh3 --- addons/sale/__openerp__.py | 3 + .../static/src/js/edi_sale_purchase_order.js | 23 +++++ .../src/xml/edi_sale_purchase_order.xml | 96 +++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 addons/sale/static/src/js/edi_sale_purchase_order.js create mode 100644 addons/sale/static/src/xml/edi_sale_purchase_order.xml diff --git a/addons/sale/__openerp__.py b/addons/sale/__openerp__.py index ae71e2c0bce..abb1f719158 100644 --- a/addons/sale/__openerp__.py +++ b/addons/sale/__openerp__.py @@ -103,6 +103,9 @@ Dashboard for Sales Manager that includes: 'test/sale_report.yml', ], + "js": [ + 'static/src/js/edi_sale_purchase_order.js', + ], 'installable': True, 'active': False, 'certificate': '0058103601429', diff --git a/addons/sale/static/src/js/edi_sale_purchase_order.js b/addons/sale/static/src/js/edi_sale_purchase_order.js new file mode 100644 index 00000000000..f0644c7407f --- /dev/null +++ b/addons/sale/static/src/js/edi_sale_purchase_order.js @@ -0,0 +1,23 @@ +openerp.web.edi_sale_purchase_order = function(openerp) { +var QWeb = new QWeb2.Engine(); + +openerp.web.edi_views.add('view_center_sale_order' , 'openerp.web.EdiViewCenterSalePurchase'); +openerp.web.edi_views.add('view_center_purchase_order' , 'openerp.web.EdiViewCenterSalePurchase'); +openerp.web.EdiViewCenterSalePurchase = openerp.web.Class.extend({ + init: function(element, edi){ + var self = this; + this.edi_document = eval(edi.document) + this.$element = element; + QWeb.add_template("/web_edi_sale_purchase/static/src/xml/edi_sale_purchase_order.xml"); + + }, + start: function() { + }, + render: function(){ + template = "OrderEdiView"; + this.$element.append($(QWeb.render(template, {'orders': this.edi_document}))); + }, + +}); +} +// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/sale/static/src/xml/edi_sale_purchase_order.xml b/addons/sale/static/src/xml/edi_sale_purchase_order.xml new file mode 100644 index 00000000000..71d15ec3ed4 --- /dev/null +++ b/addons/sale/static/src/xml/edi_sale_purchase_order.xml @@ -0,0 +1,96 @@ + From 170e6fbba1230206f742b733ede7cec4bd8eb804 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 5 Oct 2011 03:29:18 +0200 Subject: [PATCH 056/132] [FIX] account: typos in auto-export server action bzr revid: odo@openerp.com-20111005012918-pgr8qzsoudkwggol --- addons/account/edi/invoice_action_data.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/account/edi/invoice_action_data.xml b/addons/account/edi/invoice_action_data.xml index 5491e5f6e9b..32ad7e4e8ff 100644 --- a/addons/account/edi/invoice_action_data.xml +++ b/addons/account/edi/invoice_action_data.xml @@ -9,9 +9,9 @@ try: web_root_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default="") # accessing the template will fail if it has been deleted - so we'll skip everything - tmpl = self.pool.get('ir.model.data').get_object(cr, uid, 'account', 'email_template_edi_invoice')[1] + tmpl = self.pool.get('ir.model.data').get_object(cr, uid, 'account', 'email_template_edi_invoice') - edi_token = self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0] + edi_token = self.pool.get('edi.document').export_edi(cr, uid, [object], context = context)[0] context.update(edi_web_invoice_url_view='%s/web/view_edi?db=%s&token=%s' %(web_root_url, cr.dbname, edi_token)) self.pool.get('email.template').send_mail(cr, uid,tmpl.id,object.id,context=context) except: From 0cf579abe1a5f50e1d655de8dc7bee3375ab3ee8 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 5 Oct 2011 12:37:06 +0200 Subject: [PATCH 057/132] [IMP] account: edi export company_paypal_account bzr revid: odo@openerp.com-20111005103706-409wncn3oltzd5aj --- addons/account/edi/invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/edi/invoice.py b/addons/account/edi/invoice.py index de2a4e76c24..468bbfeecbb 100644 --- a/addons/account/edi/invoice.py +++ b/addons/account/edi/invoice.py @@ -47,7 +47,6 @@ class account_invoice(osv.osv, EDIMixin): 'uos_id': True, 'product_id': True, 'price_unit': True, - #'price_subtotal': True, 'quantity': True, 'discount': True, 'note': True, @@ -68,6 +67,7 @@ class account_invoice(osv.osv, EDIMixin): 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.paypal_account, #'company_logo': #TODO }) edi_doc_list.append(edi_doc) From ea8c703bbb3bbb23e186e431eaed6a7b920f7234 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 5 Oct 2011 12:41:04 +0200 Subject: [PATCH 058/132] [IMP] edi: add certificate bzr revid: odo@openerp.com-20111005104104-s8cl9bo8vz38pupz --- addons/edi/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/edi/__openerp__.py b/addons/edi/__openerp__.py index f3b11bbfadb..b46a87ee9ca 100644 --- a/addons/edi/__openerp__.py +++ b/addons/edi/__openerp__.py @@ -50,7 +50,7 @@ technical OpenERP documentation at http://doc.openerp.com ], 'installable': True, 'active': False, - #'certificate': TODO + 'certificate': '002046536359186', } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file From 72d5ab8b9954f77e71e8db6b1d07965e1617bbbf Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 5 Oct 2011 13:38:46 +0200 Subject: [PATCH 059/132] [IMP] email_template: added opt_out flag on res.partner In the context of email templates or for related features like marketing campaigns or automated business document notification, this flag may be used to disable the sending of template-based emails to the partner. It will not prevent sending an email based on a template by default, each feature using a template should implement this if needed. bzr revid: odo@openerp.com-20111005113846-qik6d5gxd4xqeqdj --- addons/email_template/__init__.py | 1 + addons/email_template/__openerp__.py | 4 +++ addons/email_template/res_partner.py | 37 ++++++++++++++++++++++ addons/email_template/res_partner_demo.yml | 9 ++++++ addons/email_template/res_partner_view.xml | 16 ++++++++++ 5 files changed, 67 insertions(+) create mode 100644 addons/email_template/res_partner.py create mode 100644 addons/email_template/res_partner_demo.yml create mode 100644 addons/email_template/res_partner_view.xml diff --git a/addons/email_template/__init__.py b/addons/email_template/__init__.py index 97931ea8bd5..d1cf5b28431 100644 --- a/addons/email_template/__init__.py +++ b/addons/email_template/__init__.py @@ -21,5 +21,6 @@ ############################################################################## import email_template import wizard +import res_partner # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/email_template/__openerp__.py b/addons/email_template/__openerp__.py index e5f83f9ac0c..575550cf2d3 100644 --- a/addons/email_template/__openerp__.py +++ b/addons/email_template/__openerp__.py @@ -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", diff --git a/addons/email_template/res_partner.py b/addons/email_template/res_partner.py new file mode 100644 index 00000000000..74d8b4e0693 --- /dev/null +++ b/addons/email_template/res_partner.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2011 OpenERP S.A. +# +# 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 . +# +############################################################################## + +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, + } \ No newline at end of file diff --git a/addons/email_template/res_partner_demo.yml b/addons/email_template/res_partner_demo.yml new file mode 100644 index 00000000000..8eaf48f9e87 --- /dev/null +++ b/addons/email_template/res_partner_demo.yml @@ -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}) \ No newline at end of file diff --git a/addons/email_template/res_partner_view.xml b/addons/email_template/res_partner_view.xml new file mode 100644 index 00000000000..4790aa7522a --- /dev/null +++ b/addons/email_template/res_partner_view.xml @@ -0,0 +1,16 @@ + + + + + res.partner.opt_out.form + res.partner + form + + + + + + + + + \ No newline at end of file From 313f60729b2c3a423f77cdc1544132904dac079b Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 5 Oct 2011 16:16:52 +0200 Subject: [PATCH 060/132] [IMP] account: add company paypal_account in edi export bzr revid: odo@openerp.com-20111005141652-43q7e2694qd8km3z --- addons/account/edi/invoice.py | 2 +- addons/edi/test/edi_partner_test.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/account/edi/invoice.py b/addons/account/edi/invoice.py index 468bbfeecbb..a650f0b4537 100644 --- a/addons/account/edi/invoice.py +++ b/addons/account/edi/invoice.py @@ -67,7 +67,7 @@ class account_invoice(osv.osv, EDIMixin): 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.paypal_account, + 'company_paypal_account': invoice.company_id.paypal_account, #'company_logo': #TODO }) edi_doc_list.append(edi_doc) diff --git a/addons/edi/test/edi_partner_test.yml b/addons/edi/test/edi_partner_test.yml index e2e497f1344..fce5e016782 100644 --- a/addons/edi/test/edi_partner_test.yml +++ b/addons/edi/test/edi_partner_test.yml @@ -16,6 +16,7 @@ # 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 From 888a44aa63d07227b22ebc0a50da8fdde53afd10 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 6 Oct 2011 13:32:13 +0200 Subject: [PATCH 061/132] [IMP] sale: reviewed/cleanedup edi + auto-email notifications bzr revid: odo@openerp.com-20111006113213-qm7tb1c39fs4nqep --- addons/sale/__init__.py | 3 +- addons/sale/__openerp__.py | 4 +- addons/sale/edi/__init__.py | 22 +++ addons/sale/edi/sale_order.py | 173 +++++++++++++++++++++ addons/sale/edi/sale_order_action_data.xml | 172 ++++++++++++++++++++ addons/sale/edi_sale_order.py | 144 ----------------- addons/sale/edi_sale_order_data.xml | 72 --------- addons/sale/test/edi_sale_order.yml | 172 ++++++++++---------- 8 files changed, 452 insertions(+), 310 deletions(-) create mode 100644 addons/sale/edi/__init__.py create mode 100644 addons/sale/edi/sale_order.py create mode 100644 addons/sale/edi/sale_order_action_data.xml delete mode 100644 addons/sale/edi_sale_order.py delete mode 100644 addons/sale/edi_sale_order_data.xml diff --git a/addons/sale/__init__.py b/addons/sale/__init__.py index 379c2bec5b8..683113d63a3 100644 --- a/addons/sale/__init__.py +++ b/addons/sale/__init__.py @@ -29,5 +29,6 @@ import sale_installer import wizard import report import company -#import edi_sale_order +import edi + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale/__openerp__.py b/addons/sale/__openerp__.py index abb1f719158..56ceb0f5ff4 100644 --- a/addons/sale/__openerp__.py +++ b/addons/sale/__openerp__.py @@ -85,11 +85,11 @@ Dashboard for Sales Manager that includes: 'stock_view.xml', 'board_sale_view.xml', 'process/sale_process.xml', - #'edi_sale_order_data.xml', + 'edi/sale_order_action_data.xml', ], 'demo_xml': ['sale_demo.xml'], 'test': [ - #'test/edi_sale_order.yml', + 'test/edi_sale_order.yml', 'test/data_test.yml', 'test/manual_order_policy.yml', 'test/prepaid_order_policy.yml', diff --git a/addons/sale/edi/__init__.py b/addons/sale/edi/__init__.py new file mode 100644 index 00000000000..566b1714ffd --- /dev/null +++ b/addons/sale/edi/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2011 OpenERP S.A. +# +# 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 . +# +############################################################################## + +import sale_order \ No newline at end of file diff --git a/addons/sale/edi/sale_order.py b/addons/sale/edi/sale_order.py new file mode 100644 index 00000000000..958dd1f70ec --- /dev/null +++ b/addons/sale/edi/sale_order.py @@ -0,0 +1,173 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2011 OpenERP S.A. +# +# 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 . +# +############################################################################## + +from datetime import datetime, timedelta +from dateutil.relativedelta import relativedelta + +from osv import fields, osv, orm +from edi import EDIMixin +from tools import DEFAULT_SERVER_DATE_FORMAT + +SALE_ORDER_LINE_EDI_STRUCT = { + 'sequence': True, + #SO: yes + #PO: No + 'name': True, + #custom: 'planned_date' + 'product_id': True, + 'product_uom': True, + 'price_unit': True, + #custom: 'product_qty' + 'discount': True, + #SO: yes + #PO: No + 'notes': True, +} + +SALE_ORDER_EDI_STRUCT = { + 'name': True, + 'origin': True, + 'company_id': True, # -> to be changed into partner + #custom: 'partner_ref' + 'date_order': True, + 'partner_id': True, + #custom: 'partner_address' + #custom: 'notes' + 'order_line': SALE_ORDER_LINE_EDI_STRUCT +} + +class sale_order(osv.osv, EDIMixin): + _inherit = 'sale.order' + + def edi_export(self, cr, uid, records, edi_struct=None, context=None): + """Exports a Sale order""" + edi_struct = dict(edi_struct or SALE_ORDER_EDI_STRUCT) + res_company = self.pool.get('res.company') + res_partner_address = self.pool.get('res.partner.address') + edi_doc_list = [] + for order in records: + # Get EDI doc based on struct. The result will also contain all metadata fields and attachments. + edi_doc = super(sale_order,self).edi_export(cr, uid, [order], edi_struct, context)[0] + edi_doc.update({ + # force trans-typing to purchase.order upon import + '__import_model': 'purchase', + '__import_module': 'purchase', + + 'company_address': res_company.edi_export_address(cr, uid, order.company_id, context=context), + 'company_paypal_account': order.company_id.paypal_account, + 'partner_address': res_partner_address.edi_export(cr, uid, [order.partner_order_id], context=context)[0], + + 'currency_id': self.edi_m2o(cr, uid, order.company_id.currency_id, context=context), + 'partner_ref': order.client_order_ref or False, + 'notes': order.note or False, + #TODO: company_logo + }) + edi_doc_list.append(edi_doc) + return edi_doc_list + + + def _edi_import_company(self, cr, uid, edi_document, context=None): + # TODO: for multi-company setups, we currently import the document in the + # user's current company, but we should perhaps foresee a way to select + # the desired company among the user's allowed companies + + self._edi_requires_attributes(('company_id','company_address'), edi_document) + res_partner_address = self.pool.get('res.partner.address') + res_partner = self.pool.get('res.partner') + + # imported company = as a 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) + partner_value = {'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'] = 'default' + address_id = res_partner_address.edi_import(cr, uid, address_info, context=context) + + # modify edi_document to refer to new partner/address + 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 + address_edi_m2o = self.edi_m2o(cr, uid, partner_address, context=context) + edi_document['partner_order_id'] = address_edi_m2o + edi_document['partner_invoice_id'] = address_edi_m2o + edi_document['partner_shipping_id'] = address_edi_m2o + + return partner_id + + def edi_get_pricelist(self, cr, uid, partner_id, context=None): + # value = ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.list0", "Public Pricelist (EUR)"] + partner_model = self.pool.get('res.partner') + partner = partner_model.browse(cr, uid, partner_id, context=context) + pricelist = partner.property_product_pricelist + if not pricelist: + pricelist = self.pool.get('ir.model.data').get_object(cr, uid, 'product', 'list0', context=context) + return self.edi_m2o(cr, uid, pricelist, context=context) + + def edi_import(self, cr, uid, edi_document, context=None): + self._edi_requires_attributes(('company_id','company_address','order_line','date_order'), edi_document) + + #import company as a new partner + partner_id = self._edi_import_company(cr, uid, edi_document, context=context) + + date_order = edi_document['date_order'] + edi_document['client_order_ref'] = edi_document.pop('partner_ref', False) + edi_document['note'] = edi_document.pop('notes', False) + edi_document['pricelist_id'] = self.edi_get_pricelist(cr, uid, partner_id, context=context) + order_lines = edi_document['order_line'] + for order_line in order_lines: + order_line['product_uom_qty'] = order_line['product_qty'] + del order_line['product_qty'] + date_planned = order_line.pop('date_planned') + delay = 0 + if date_order and date_planned: + # no security_days buffer, this is the promised date given by supplier + delay = (datetime.strptime(date_planned, DEFAULT_SERVER_DATE_FORMAT) - \ + datetime.strptime(date_order, DEFAULT_SERVER_DATE_FORMAT)).days + order_line['delay'] = delay + return super(sale_order,self).edi_import(cr, uid, edi_document, context=context) + +class sale_order_line(osv.osv, EDIMixin): + _inherit='sale.order.line' + + def edi_export(self, cr, uid, records, edi_struct=None, context=None): + edi_struct = dict(edi_struct or SALE_ORDER_LINE_EDI_STRUCT) + edi_doc_list = [] + for line in records: + edi_doc = super(sale_order_line,self).edi_export(cr, uid, [line], edi_struct, context)[0] + edi_doc['product_qty'] = line.product_uom_qty + if line.product_uos: + edi_doc.update(product_uom=line.product_uos, + product_qty=line.product_uos_qty) + + # company.security_days is for internal use, so customer should only + # see the expected date_planned based on line.delay + date_planned = datetime.strptime(line.order_id.date_order, DEFAULT_SERVER_DATE_FORMAT) + \ + relativedelta(days=line.delay or 0.0) + edi_doc['date_planned'] = date_planned.strftime(DEFAULT_SERVER_DATE_FORMAT) + edi_doc_list.append(edi_doc) + return edi_doc_list + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/addons/sale/edi/sale_order_action_data.xml b/addons/sale/edi/sale_order_action_data.xml new file mode 100644 index 00000000000..6d46006f384 --- /dev/null +++ b/addons/sale/edi/sale_order_action_data.xml @@ -0,0 +1,172 @@ + + + + + + ") + + # accessing the template will fail if it has been deleted - so we'll skip everything + tmpl = self.pool.get('ir.model.data').get_object(cr, uid, 'sale', 'email_template_edi_sale') + + edi_token = self.pool.get('edi.document').export_edi(cr, uid, [object], context = context)[0] + context.update(edi_web_url_view='%s/web/view_edi?db=%s&token=%s' %(web_root_url, cr.dbname, edi_token)) + self.pool.get('email.template').send_mail(cr, uid,tmpl.id,object.id,context=context) +except: + pass + ]]> + + code + ir.actions.server + + True + Auto-email confirmed sale orders + + + + + + + + + + + + + + + Automated Sale Order Notification Mail + ${object.user_id.user_email or ''} + ${object.company_id.name} Order (Ref ${object.name or 'n/a' }) + ${object.partner_invoice_id.email} + + + +

Hello${object.partner_order_id.name and ' ' or ''}${object.partner_order_id.name or ''},

+ +

Your order for ${object.partner_id.name} has just been validated:

+ +

+   REFERENCES
+   Order number: ${object.name}
+   Order total: ${object.amount_total} ${object.pricelist_id.currency_id.name}
+   Order date: ${object.date_order}
+ % if object.origin: +   Order reference: ${object.origin}
+ % endif + % if object.client_order_ref: +   Your reference: ${object.client_order_ref}
+ % endif +   Your contact: ${object.user_id.name} +

+ +

+ You can view the order confirmation document, download it and pay online using the following link: +

+ View Order + + % if object.company_id.paypal_account: +
+

It is also possible to directly pay with Paypal:

+ Pay with Paypal + % endif + +
+

If you have any question, do not hesitate to contact us.

+

Thank you for choosing ${object.company_id.name or 'us'}!

+
+
+
+

+ ${object.company_id.name}

+
+
+ + % if object.company_id.street: + ${object.company_id.street}
+ % 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} ${object.company_id.city}
+ % 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: + + %endif +

+
+
+ ]]> + + % endif + | Your contact: ${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''} + +You can view the order confirmation, download it and even pay online using the following link: + ${ctx.get('edi_web_url_view') or 'n/a'} + +% if object.company_id.paypal_account: +It is also possible to directly pay with Paypal: + ${"https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Order%%20%s&invoice=%s&amount=%s¤cy_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Order_PayNow_%s"%(object.company_id.paypal_account,object.name and object.name.replace('/','%2f') or '', object.name and object.name.replace('/','%2f') or '', object.amount_total, object.pricelist_id.currency_id.name, object.pricelist_id.currency_id.name)} +% 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 + ]]> + + + diff --git a/addons/sale/edi_sale_order.py b/addons/sale/edi_sale_order.py deleted file mode 100644 index 7cec8f39374..00000000000 --- a/addons/sale/edi_sale_order.py +++ /dev/null @@ -1,144 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2009 Tiny SPRL (). -# -# 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 . -# -############################################################################## - -from osv import fields, osv, orm -from base.ir import ir_edi -from tools.translate import _ -from datetime import date, datetime - -class sale_order(osv.osv, ir_edi.edi): - _inherit = 'sale.order' - - def edi_export(self, cr, uid, records, edi_struct=None, context=None): - """Exports a Sale order""" - edi_struct = { - 'company_id': True, # -> to be changed into partner - 'name': True, - 'client_order_ref': True, - 'origin': True, - 'date_order': True, - 'partner_id': True, - 'partner_order_id': True, #only one address needed - #SO: 'partner_order_id' - #PO: 'partner_address_id' - 'pricelist_id': True, - 'note': True, - #SO: 'note' - #PO: 'notes' - 'amount_total': True, - 'amount_tax': True, - 'amount_untaxed': True, - 'order_line': { - 'sequence': True, - #SO: yes - #PO: No - 'name': True, - 'delay': True, - #SO: 'delay' : 'date_order' - 'date_planned' - #PO: 'date_planned': 'date_order' + 'delay' - - 'product_id': True, - 'product_uom': True, - 'price_unit': True, - 'product_uom_qty': True, - #SO: 'product_uom_qty' - #PO: 'product_qty' - 'price_subtotal': True, - 'discount': True, - #SO: yes - #PO: No - 'notes': True, - } - } - res_company = self.pool.get('res.company') - edi_doc_list = [] - for order in records: - # Get EDI doc based on struct. The result will also contain all metadata fields and attachments. - edi_doc = super(sale_order,self).edi_export(cr, uid, [order], edi_struct, context)[0] - currency = order.company_id.currency_id - edi_address = res_company.edi_export_address(cr, uid, [order.company_id], context=context)[0] - edi_doc.update(company_address=edi_address, - currency_id=(currency and self.edi_m2o(cr, uid, currency, context=context) or False)) - #TODO: company_logo - edi_doc_list.append(edi_doc) - return edi_doc_list - - - def edi_import_company(self, cr, uid, edi_document, context=None): - partner_address_pool = self.pool.get('res.partner.address') - partner_pool = self.pool.get('res.partner') - company_pool = self.pool.get('res.company') - - # import company as a new partner, supplier=1. - # company_address data used to add address to new partner - partner_value = {'supplier': True} - partner_id = company_pool.edi_import_as_partner(cr, uid, edi_document, values=partner_value, context=context) - - - # partner_id field is modified to point to the new partner - res = partner_pool.address_get(cr, uid, [partner_id], ['contact', 'invoice']) - address_id = res['invoice'] - partner = partner_pool.browse(cr, uid, partner_id, context=context) - partner_address = partner_address_pool.browse(cr, uid, address_id, context=context) - edi_document['partner_id'] = self.edi_m2o(cr, uid, partner, context=context) - edi_document['partner_order_id'] = self.edi_m2o(cr, uid, partner_address, context=context) - edi_document['partner_invoice_id'] = edi_document['partner_order_id'] - edi_document['partner_shipping_id'] = edi_document['partner_order_id'] - del edi_document['company_id'] - return partner_id - - def edi_get_pricelist(self, cr, uid, partner_id, context=None): - # value = ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.list0", "Public Pricelist (EUR)"] - partner_model = self.pool.get('res.partner') - partner = partner_model.browse(cr, uid, partner_id, context=context) - pricelist = partner.property_product_pricelist - if not pricelist: - pricelist = self.pool.get('ir.model.data').get_object(cr, uid, 'product', 'list0', context=context) - return self.edi_m2o(cr, uid, pricelist, context=context) - - def edi_import(self, cr, uid, edi_document, context=None): - if context is None: - context = {} - - #import company as a new partner - partner_id = self.edi_import_company(cr, uid, edi_document, context=context) - - date_order = edi_document.get('date_order', False) - edi_document['client_order_ref'] = edi_document['name'] - edi_document['note'] = edi_document.get('notes', False) - edi_document['pricelist_id'] = self.edi_get_pricelist(cr, uid, partner_id, context=context) - order_lines = edi_document['order_line'] - for order_line in order_lines: - order_line['product_uom_qty'] = order_line['product_qty'] - date_planned = order_line['date_planned'] - delay = 0 - if date_order and date_planned: - delay = (datetime.strptime(date_planned, "%Y-%m-%d") - datetime.strptime(date_order, "%Y-%m-%d")).days - order_line['delay'] = delay - return super(sale_order,self).edi_import(cr, uid, edi_document, context=context) - -sale_order() - -class sale_order_line(osv.osv, ir_edi.edi): - _inherit='sale.order.line' - -sale_order_line() -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale/edi_sale_order_data.xml b/addons/sale/edi_sale_order_data.xml deleted file mode 100644 index 33b65b4c031..00000000000 --- a/addons/sale/edi_sale_order_data.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - context.update({'edi_web_sale_url_view': '%s/web/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) -if not object.partner_id.opt_out: self.pool.get('email.template').send_mail(cr, - uid, - self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sale', 'email_template_edi_sale')[1], - object.id, - context=context) - - code - ir.actions.server - - True - EDI Document - Sale Order - - - - - - - - - - - - Mail Template of Sale Order For EDI Document - ${object.user_id.user_email or ''} - ${object.user_id.user_email or ''} - ${object.company_id.name} - Sale Order ${object.name} - ${object.partner_invoice_id.email} - - -<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); "> - -<p> Hello ${object.partner_invoice_id.name and ' ' or ''},</p> -<p> You can click on the following link to preview sale order : <br/> - <a href="${object._context.get('edi_web_sale_url_view')}">${object._context.get('edi_web_sale_url_view')} </a> -</p> - - -<p style="border-left: 1px solid #8e0000; margin-left: 30px;"> <strong>REFERENCES</strong><br /> Order number: <strong>${object.name}</strong><br /> Order amount: <strong>${object.amount_total} </strong><br /> Order date: ${object.date_order or 'n/a'}<br /> Your contact: <a href="mailto:${object.user_id.user_email or ''}?subject=Order%20${object.name}">${object.user_id.name}</a></p> - - -<p> If you have any question, do not hesitate to reply directly to this e-mail.</p> <p> Thank you for choosing OpenERP!<br /> </p> <div style="width: 375px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; overflow-x: hidden; overflow-y: hidden; zoom: 1; background-image: url(http://www.openerp.com/sites/default/files/red_gradient_bg.png); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(142, 0, 0); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-bottom-right-radius: 0px 0px; border-bottom-left-radius: 0px 0px; background-position: 0% 0%; background-repeat: repeat no-repeat; "> <h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 9px; padding-right: 14px; padding-bottom: 9px; padding-left: 14px; font-size: 12px; font-weight: normal; font-style: normal; color: rgb(255, 255, 255); "> <strong>${object.company_id.name}</strong></h3> </div> <div style="width: 347px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 12px; padding-right: 14px; padding-bottom: 12px; padding-left: 14px; overflow-x: hidden; overflow-y: hidden; zoom: 1; line-height: 16px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(242, 242, 242); "> <div> Contact:<a href="mailto:${object.user_id.user_email or ''}?subject=Order%20${object.name}">${object.user_id.name}</a></div> <div> </div> </div> </div> <p> </p> - - -Hello ${object.partner_invoice_id.name and ' ' or ''}, - -You can click on the following link to preview sale order: - - ${object._context.get('edi_web_sale_url_view') or 'n/a'} - -Order Number: *${object.name}* -Amount: *${object.amount_total}* -Order date: ${object.date_order or 'n/a'} -Your contact: ${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''} - - -If you have any question, do not hesitate to reply directly to this e-mail. - -Thank you for choosing our service! - --- -${object.company_id.name} -Contact: ${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''} - - - - diff --git a/addons/sale/test/edi_sale_order.yml b/addons/sale/test/edi_sale_order.yml index cf8da5f1efb..d7a8f4806f3 100644 --- a/addons/sale/test/edi_sale_order.yml +++ b/addons/sale/test/edi_sale_order.yml @@ -1,127 +1,117 @@ - - I create a partner which is a my customer + I create a draft Sale Order - - !record {model: res.partner, id: res_partner_test22}: - name: Junjun wala - supplier: False - customer: True - opt_out: True -- - I create one Sale Order -- - !record {model: sale.order, id: sale_order_test}: - partner_id: res_partner_test22 - partner_invoice_id: base.res_partner_address_3 - partner_order_id: base.res_partner_address_3 - partner_shipping_id: base.res_partner_address_3 + !record {model: sale.order, id: sale_order_edi_1}: + partner_id: base.res_partner_agrolait + partner_invoice_id: base.res_partner_address_8invoice + partner_order_id: base.res_partner_address_8invoice + partner_shipping_id: base.res_partner_address_8invoice pricelist_id: 1 order_line: - product_id: product.product_product_pc1 product_uom_qty: 1.0 product_uom: 1 price_unit: 150.0 - name: 'basic pc' + name: 'Basic pc' order_line: - product_id: product.product_product_pc3 product_uom_qty: 10.0 product_uom: 1 price_unit: 200.0 - name: 'medium pc' + name: 'Medium pc' - - I Open the sale order + I confirm the sale order - - !python {model: sale.order}: | - - orders = self.browse(cr, uid, ref("sale_order_test")) - import netsvc - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'sale.order',orders.id,'order_confirm', cr) - + !workflow {model: sale.order, ref: sale_order_edi_1, action: order_confirm} - - I Tesing of EDI functionality. First I export Sale Order from my company than import that Order into customer company + Then I export the sale order via EDI - - !python {model: ir.edi.document}: | - sale_order_pool = self.pool.get('sale.order') - orders = sale_order_pool.browse(cr, uid, ref("sale_order_test")) - - tokens = self.export_edi(cr, uid, [orders]) - assert tokens, 'Token is not generated' - + !python {model: edi.document}: | + sale_order = self.pool.get('sale.order') + so = sale_order.browse(cr, uid, ref("sale_order_edi_1")) + token = self.export_edi(cr, uid, [so]) + assert token, 'Invalid EDI Token' - - I import Sale order from EDI Document of purchase order + Then I import a sample EDI document of a purchase order - - !python {model: ir.edi.document}: | + !python {model: edi.document}: | sale_order_pool = self.pool.get('sale.order') edi_document = { - "order_line": [{ - "name": "basic pc", - "product_uom": ["5af1272e-dd26-11e0-b65e-701a04e25543:product.product_uom_unit", "PCE"], - "date_planned": "2011-09-30", - "price_unit": 150.0, - "__last_update": False, - "__id": "5af1272e-dd26-11e0-b65e-701a04e25543:purchase.purchase_order_line-AlhsVDZGoKvJ", - "product_qty": 1.0, - "product_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:product.product_product_pc1", "[PC1] Basic PC"] - }, - { - "name": "medium pc", - "product_uom": ["5af1272e-dd26-11e0-b65e-701a04e25543:product.product_uom_unit", "PCE"], - "date_planned": "2011-09-15", - "price_unit": 100.0, - "__last_update": False, - "__id": "5af1272e-dd26-11e0-b65e-701a04e25543:purchase.purchase_order_line-AlhsVDZGoKvJ", - "product_qty": 2.0, - "product_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:product.product_product_pc3", "[PC3] Medium PC"] - }], - "partner_address_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:base.res_partner_address_11", "Sebastien LANGE, France, Alencon, 1 place de l'\u00c9glise"], - "__id": "5af1272e-dd26-11e0-b65e-701a04e25543:purchase.purchase_order_test", - "date_order": "2011-09-12", - "partner_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:purchase.res_partner_test20", "jones white"], - "__attachments": [], - "__module": "sale", - "amount_total": 350.0, + "__id": "purchase:5af1272e-dd26-11e0-b65e-701a04e25543.purchase_order_test", + "__module": "purchase", + "__model": "purchase.order", + "__import_module": "sale", + "__import_model": "sale.order", + "__version": [6,1,0], + "name": "PO00011", + "date_order": "2011-09-12", + "company_id": ["base:5af1272e-dd26-11e0-b65e-701a04e25543.main_company", "Client S.A."], "company_address": { + "__id": "base:5af1272e-dd26-11e0-b65e-701a04e25543.some_address", + "__module": "base", + "__model": "res.partner.address", "phone": "(+32).81.81.37.00", "street": "Chaussee de Namur 40", "city": "Gerompont", "zip": "1367", - "__last_update": False, - "country_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:base.be", "Belgium"], - "__id": "5af1272e-dd26-11e0-b65e-701a04e25543:base.main_address", + "country_id": ["base:5af1272e-dd26-11e0-b65e-701a04e25543.be", "Belgium"], }, - "amount_untaxed": 350.0, - "name": "PO00011", - "__model": "sale.order", - "__last_update": False, - "company_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:base.main_company", "Client S.A."], - "__version": [6, 1], - "pricelist_id": ["5af1272e-dd26-11e0-b65e-701a04e25543:product.list0", "Public Pricelist (EUR)"] + "partner_id": ["purchase:5af1272e-dd26-11e0-b65e-701a04e25543.res_partner_test20", "jones white"], + "partner_address": { + "__id": "base:5af1272e-dd26-11e0-b65e-701a04e25543.res_partner_address_7wdsjasdjh", + "__module": "base", + "__model": "res.partner.address", + "phone": "(+32).81.81.37.00", + "street": "Chaussee de Namur 40", + "city": "Gerompont", + "zip": "1367", + "country_id": ["base:5af1272e-dd26-11e0-b65e-701a04e25543.be", "Belgium"], + }, + "order_line": [{ + "__id": "purchase:5af1272e-dd26-11e0-b65e-701a04e25543.purchase_order_line-AlhsVDZGoKvJ", + "__module": "purchase", + "__model": "purchase.order.line", + "__import_module": "sale", + "__import_model": "sale.order.line", + "name": "Basic PC", + "date_planned": "2011-09-30", + "price_unit": 150.0, + "product_id": ["product:5af1272e-dd26-11e0-b65e-701a04e25543.product_product_pc1", "[PC1] Basic PC"], + "product_qty": 1.0, + "product_uom": ["product:5af1272e-dd26-11e0-b65e-701a04e25543.product_uom_unit", "PCE"], + }, + { + "__id": "purchase:5af1272e-dd26-11e0-b65e-701a04e25543.purchase_order_line-Alsads33e", + "__module": "purchase", + "__model": "purchase.order.line", + "__import_module": "sale", + "__import_model": "sale.order.line", + "name": "Medium PC", + "date_planned": "2011-09-15", + "price_unit": 100.0, + "product_id": ["product:5af1272e-dd26-11e0-b65e-701a04e25543.product_product_pc3", "[PC3] Medium PC"], + "product_qty": 2.0, + "product_uom": ["product:5af1272e-dd26-11e0-b65e-701a04e25543.product_uom_unit", "PCE"], + }], } new_sale_order_id = sale_order_pool.edi_import(cr, uid, edi_document, context=context) - assert new_sale_order_id, 'Sale order is not imported' + assert new_sale_order_id, 'Sale order import failed' + order_new = sale_order_pool.browse(cr, uid, new_sale_order_id) -- - I Checking the sale order become purchase order or not after import -- - !python {model: sale.order}: | - - ids = self.search(cr, uid, [('partner_id','=','Client S.A.'), ('client_order_ref', '=', 'PO00011')]) - assert ids, 'Order does not have created of party' - order_new = self.browse(cr, uid, ids[0]) - assert order_new.pricelist_id.name == 'Public Pricelist' , "Public Price list is not imported" - assert order_new.amount_total == 350, "Amount total is not same" - assert order_new.amount_untaxed == 350, "untaxed amount is not same" - assert len(order_new.order_line) == 2, "sale lines are not same" + assert order_new.pricelist_id.name == 'Public Pricelist' , "Public Price list was not automatically assigned" + assert order_new.amount_total == 350, "Amount total is wrong" + assert order_new.amount_untaxed == 350, "Untaxed amount is wrong" + assert len(order_new.order_line) == 2, "Sale order lines mismatch" for sale_line in order_new.order_line: - if sale_line.name == 'basic pc': - assert sale_line.delay == 18 , "delay is not same" + if sale_line.name == 'Basic PC': + assert sale_line.delay == 18 , "incorrect delay: got %s, expected 18"%(sale_line.delay,) assert sale_line.product_uom.name == "PCE" , "uom is not same" - assert sale_line.price_unit == 150 , "price unit is not same" + assert sale_line.price_unit == 150 , "unit price is not same, got %s, expected 150"%(sale_line.price_unit,) assert sale_line.product_uom_qty == 1 , "product qty is not same" - elif sale_line.name == 'medium pc': - assert sale_line.delay == 3 , "delay is not same" + elif sale_line.name == 'Medium PC': + assert sale_line.delay == 3 , "incorrect delay: got %s, expected 3"%(sale_line.delay,) assert sale_line.product_uom.name == "PCE" , "uom is not same" - assert sale_line.price_unit == 100 , "price unit is not same" + assert sale_line.price_unit == 100 , "unit price is not same, got %s, expected 100"%(sale_line.price_unit,) assert sale_line.product_uom_qty == 2 , "product qty is not same" else: - assert 'wrong product imported in sale lines' + raise AssertionError('unknown order line: %s' % sale_line) \ No newline at end of file From e47fbc56ea6e29620ecb9c6bd6df024565d1e7a5 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 6 Oct 2011 13:33:19 +0200 Subject: [PATCH 062/132] [IMP] edi: support for __import_module and __import_model metadata This is useful for transtyping documents, for example a sale.order must be converted to a purchase.order when it is imported via EDI. bzr revid: odo@openerp.com-20111006113319-ll5rfavzwzftkydb --- addons/edi/models/edi.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/addons/edi/models/edi.py b/addons/edi/models/edi.py index 2394dd7012e..2cc83eb3483 100644 --- a/addons/edi/models/edi.py +++ b/addons/edi/models/edi.py @@ -141,13 +141,14 @@ class edi_document(osv.osv): ir_module = self.pool.get('ir.module.module') res = [] for edi_document in edi_documents: - module = edi_document.get('__module') + 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. " - "The OpenERP configuration assistant will help with this if you are connected as an administrator.")%(module,)) - model = edi_document.get('__model') - assert model, '__model attribute is required in each EDI document' + "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) record_id = model_obj.edi_import(cr, uid, edi_document, context=context) res.append((model, record_id)) @@ -538,9 +539,10 @@ class EDIMixin(object): * After finding the database ID of the final record in the database, connect it to the parent record. """ - assert self._name == edi_document['__model'],\ - "EDI Document Model and current model do not match: '%s' (EDI) vs '%s' (current)" % \ - (edi_document['__model'], self._name) + 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']) From a173d8aa7c9293c9592d21c0854695c0662307aa Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 6 Oct 2011 13:33:53 +0200 Subject: [PATCH 063/132] [FIX] email_template: typo in partner view bzr revid: odo@openerp.com-20111006113353-ksb490zpxtvx4qio --- addons/email_template/res_partner_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/email_template/res_partner_view.xml b/addons/email_template/res_partner_view.xml index 4790aa7522a..0d2f919a96e 100644 --- a/addons/email_template/res_partner_view.xml +++ b/addons/email_template/res_partner_view.xml @@ -7,7 +7,7 @@ form - + From 7550c5c6f49a9837e6036d5d631bcd30d1cfedda Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 6 Oct 2011 13:36:51 +0200 Subject: [PATCH 064/132] [IMP] account: various fixes/review in EDI + auto-email bzr revid: odo@openerp.com-20111006113651-3otsv1n2cwc56xk1 --- addons/account/edi/invoice.py | 95 ++++++++++++---------- addons/account/edi/invoice_action_data.xml | 28 ++++--- addons/account/test/test_edi_invoice.yml | 23 ++++-- 3 files changed, 84 insertions(+), 62 deletions(-) diff --git a/addons/account/edi/invoice.py b/addons/account/edi/invoice.py index a650f0b4537..817bf583516 100644 --- a/addons/account/edi/invoice.py +++ b/addons/account/edi/invoice.py @@ -21,53 +21,59 @@ from osv import fields, osv, orm from edi import EDIMixin -from tools.translate import _ + +INVOICE_LINE_EDI_STRUCT = { + 'name': True, + 'origin': True, + 'uos_id': True, + 'product_id': True, + 'price_unit': True, + 'quantity': True, + 'discount': True, + 'note': 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, + 'currency_id': True, # TODO: should perhaps include sample rate + rounding + 'invoice_line': INVOICE_LINE_EDI_STRUCT, + 'tax_line': INVOICE_TAX_LINE_EDI_STRUCT, +} 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 = { - '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, - 'address_invoice_id': True, #only one address needed - 'payment_term': True, - 'currency_id': True, # TODO: should perhaps include sample rate + rounding - 'invoice_line': { - 'name': True, - 'origin': True, - 'uos_id': True, - 'product_id': True, - 'price_unit': True, - 'quantity': True, - 'discount': True, - 'note': True, - }, - 'tax_line': { - 'name': True, - 'base': True, - 'amount': True, - 'manual': True, - 'sequence': True, - 'base_amount': True, - 'tax_amount': True, - }, - } + 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: 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], #'company_logo': #TODO }) edi_doc_list.append(edi_doc) @@ -101,33 +107,36 @@ class account_invoice(osv.osv, EDIMixin): 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 - company_id, company_name = edi_document['company_id'] - partner_id = self.edi_import_relation(cr, uid, 'res.partner', company_name, - company_id, context=context) + 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}) - partner_id = res_partner.write(cr, uid, [partner_id], partner_value, context=context) + res_partner.write(cr, uid, [partner_id], partner_value, context=context) # imported company_address = new partner address - address_info = edi_document['company_address'] - address_info['partner_id'] = (company_id, company_name) + 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 - del edi_document['company_id'] - del edi_document['company_address'] partner_address = res_partner_address.browse(cr, uid, address_id, context=context) - edi_document['partner_id'] = (company_id, company_name) + 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 diff --git a/addons/account/edi/invoice_action_data.xml b/addons/account/edi/invoice_action_data.xml index 32ad7e4e8ff..1152d0bada5 100644 --- a/addons/account/edi/invoice_action_data.xml +++ b/addons/account/edi/invoice_action_data.xml @@ -12,7 +12,7 @@ try: tmpl = self.pool.get('ir.model.data').get_object(cr, uid, 'account', 'email_template_edi_invoice') edi_token = self.pool.get('edi.document').export_edi(cr, uid, [object], context = context)[0] - context.update(edi_web_invoice_url_view='%s/web/view_edi?db=%s&token=%s' %(web_root_url, cr.dbname, edi_token)) + context.update(edi_web_url_view='%s/web/view_edi?db=%s&token=%s' %(web_root_url, cr.dbname, edi_token)) self.pool.get('email.template').send_mail(cr, uid,tmpl.id,object.id,context=context) except: pass @@ -22,7 +22,7 @@ except: ir.actions.server True - EDI Document - Invoice + Auto-email confirmed invoices @@ -51,9 +51,11 @@ except:

  REFERENCES
  Invoice number: ${object.number}
-   Invoice amount: ${object.amount_total} ${object.currency_id.name}
-   Invoice date: ${object.date_invoice or 'n/a'}
-   Order reference: ${object.origin or 'n/a'}
+   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}

@@ -61,7 +63,7 @@ except: You can view the invoice document, download it and pay online using the following link:

View Invoice + href="${ctx.get('edi_web_url_view') or ''}">View Invoice % if object.company_id.paypal_account:
@@ -113,18 +115,20 @@ except: 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}* - | Amount: *${object.amount_total} ${object.currency_id.name}* - | Invoice date: ${object.date_invoice or 'n/a'} - | Order reference: ${object.origin or 'n/a'} + | 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_invoice_url_view') or 'n/a'} + ${ctx.get('edi_web_url_view') or 'n/a'} % if object.company_id.paypal_account: It is also possible to directly pay with Paypal: - ${"https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Invoice%%20%s&invoice=%s&amount=%s&currency_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s"%(object.company_id.paypal_account, object.number and object.number.replace('/','%2f') or '', object.number and object.number.replace('/','%2f') or '', object.amount_total, object.currency_id.name, object.currency_id.name)} + ${"https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Invoice%%20%s&invoice=%s&amount=%s¤cy_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s"%(object.company_id.paypal_account, object.number and object.number.replace('/','%2f') or '', object.number and object.number.replace('/','%2f') or '', object.amount_total, object.currency_id.name, object.currency_id.name)} % endif If you have any question, do not hesitate to contact us. diff --git a/addons/account/test/test_edi_invoice.yml b/addons/account/test/test_edi_invoice.yml index 62d57988c9e..23c29b6ac97 100644 --- a/addons/account/test/test_edi_invoice.yml +++ b/addons/account/test/test_edi_invoice.yml @@ -66,8 +66,17 @@ }, "company_id": ["account:b22acf7a-ddcd-11e0-a4db-701a04e25543.res_company_test11", "Thomson pvt. ltd."], "currency_id": ["base:b22acf7a-ddcd-11e0-a4db-701a04e25543.EUR", "EUR (€)"], - "address_invoice_id": ["base:b22acf7a-ddcd-11e0-a4db-701a04e25543.res_partner_address_11", "Sebastien LANGE, France, Alencon, 1 place de l'\u00c9glise"], - "partner_id": ["account:b22acf7a-ddcd-11e0-a4db-701a04e25543.res_partner_test20", "Junjun wala"], + "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": [{ @@ -111,18 +120,18 @@ 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': + 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': + 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: - assert 'wrong product imported in invoice lines' + raise AssertionError('unknown invoice line: %s' % inv_line) for inv_tax in invoice_new.tax_line: - assert inv_tax.manual, "for tax line manual is not set to True" - assert inv_tax.account_id, "for tax_line default accounts is not picked based on the tax config of the DB where it is imported." + assert inv_tax.manual, "tax line not set to manual" + assert inv_tax.account_id, "missing tax line account" From 916032cd6c8c69dff7bfb40d952ed85b22d146f2 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 6 Oct 2011 13:47:44 +0200 Subject: [PATCH 065/132] [FIX] edi: web.common.dispatch -> web.common.http bzr revid: odo@openerp.com-20111006114744-qz0igxl6epe3sz3t --- addons/edi/controllers/edi_get.py | 2 +- addons/edi/controllers/edi_import.py | 2 +- addons/edi/controllers/edi_view.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/edi/controllers/edi_get.py b/addons/edi/controllers/edi_get.py index 1e8f61708c8..b53462b2a31 100644 --- a/addons/edi/controllers/edi_get.py +++ b/addons/edi/controllers/edi_get.py @@ -1,5 +1,5 @@ import werkzeug.wrappers -import web.common.dispatch as openerpweb +import web.common.http as openerpweb import json class EDIGet(openerpweb.Controller): diff --git a/addons/edi/controllers/edi_import.py b/addons/edi/controllers/edi_import.py index 3f99f6cdfbb..a1d1c9496ef 100644 --- a/addons/edi/controllers/edi_import.py +++ b/addons/edi/controllers/edi_import.py @@ -1,5 +1,5 @@ import werkzeug.wrappers -import web.common.dispatch as openerpweb +import web.common.http as openerpweb import web.controllers.main as web import json import textwrap diff --git a/addons/edi/controllers/edi_view.py b/addons/edi/controllers/edi_view.py index e2e73c05119..c1137dcaf08 100644 --- a/addons/edi/controllers/edi_view.py +++ b/addons/edi/controllers/edi_view.py @@ -1,5 +1,5 @@ import werkzeug.wrappers -import web.common.dispatch as openerpweb +import web.common.http as openerpweb import web.controllers.main as web import json import textwrap From 2fe81d35676f06d671e1acc1dd8308b21adf0aba Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 6 Oct 2011 18:33:42 +0200 Subject: [PATCH 066/132] [IMP] edi: better user feedback for import errors bzr revid: odo@openerp.com-20111006163342-4ah7fh7dwamegt6z --- addons/edi/models/edi.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/addons/edi/models/edi.py b/addons/edi/models/edi.py index 2cc83eb3483..6f8a7d32e31 100644 --- a/addons/edi/models/edi.py +++ b/addons/edi/models/edi.py @@ -208,9 +208,10 @@ class EDIMixin(object): 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, self._name) + '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 @@ -263,8 +264,12 @@ class EDIMixin(object): module, ext_id = ext_id.split('.') if not ':' in module: # this record was not previously EDI-imported - assert module == record._original_module,\ - 'Mismatching module: expected %s, got %s' % (module, record._original_module) + 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) @@ -557,7 +562,11 @@ class EDIMixin(object): # skip metadata and empty fields if field_name.startswith('__') or field_value is None or field_value is False: continue - field = self._all_columns[field_name].column + 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)) From 67cfe511f431c1445a614a0e6d72191ca3e43d7e Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 6 Oct 2011 18:34:25 +0200 Subject: [PATCH 067/132] [IMP] purchase: review/cleanup edi + auto-email bzr revid: odo@openerp.com-20111006163425-2fzo1mi6hyzdwqqs --- addons/purchase/__init__.py | 3 +- addons/purchase/__openerp__.py | 4 +- addons/purchase/edi/__init__.py | 22 +++ addons/purchase/edi/purchase_order.py | 180 ++++++++++++++++++ .../edi/purchase_order_action_data.xml | 169 ++++++++++++++++ addons/purchase/edi_purchase_order.py | 157 --------------- addons/purchase/edi_purchase_order_data.xml | 73 ------- addons/purchase/test/edi_purchase_order.yml | 167 ++++++++-------- 8 files changed, 456 insertions(+), 319 deletions(-) create mode 100644 addons/purchase/edi/__init__.py create mode 100644 addons/purchase/edi/purchase_order.py create mode 100644 addons/purchase/edi/purchase_order_action_data.xml delete mode 100644 addons/purchase/edi_purchase_order.py delete mode 100644 addons/purchase/edi_purchase_order_data.xml diff --git a/addons/purchase/__init__.py b/addons/purchase/__init__.py index 623eae1d6a9..66f92a60831 100644 --- a/addons/purchase/__init__.py +++ b/addons/purchase/__init__.py @@ -26,6 +26,7 @@ import wizard import report import stock import company -#import edi_purchase_order +import edi + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/purchase/__openerp__.py b/addons/purchase/__openerp__.py index d8415fc17c2..2953e8e3d89 100644 --- a/addons/purchase/__openerp__.py +++ b/addons/purchase/__openerp__.py @@ -58,7 +58,7 @@ Dashboard for purchase management that includes: 'process/purchase_process.xml', 'report/purchase_report_view.xml', 'board_purchase_view.xml', - #'edi_purchase_order_data.xml', + 'edi/purchase_order_action_data.xml', ], 'test': [ 'test/purchase_from_order.yml', @@ -67,7 +67,7 @@ Dashboard for purchase management that includes: 'purchase_unit_test.xml', 'test/procurement_buy.yml', 'test/purchase_report.yml', - #'test/edi_purchase_order.yml', + 'test/edi_purchase_order.yml', ], 'demo': ['purchase_demo.xml'], 'installable': True, diff --git a/addons/purchase/edi/__init__.py b/addons/purchase/edi/__init__.py new file mode 100644 index 00000000000..cfac0182d76 --- /dev/null +++ b/addons/purchase/edi/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2011 OpenERP S.A. +# +# 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 . +# +############################################################################## + +import purchase_order \ No newline at end of file diff --git a/addons/purchase/edi/purchase_order.py b/addons/purchase/edi/purchase_order.py new file mode 100644 index 00000000000..4ac25adf23b --- /dev/null +++ b/addons/purchase/edi/purchase_order.py @@ -0,0 +1,180 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2011 OpenERP S.A. +# +# 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 . +# +############################################################################## + +from datetime import datetime, timedelta +from dateutil.relativedelta import relativedelta + +from osv import fields, osv, orm +from edi import EDIMixin +from tools import DEFAULT_SERVER_DATE_FORMAT +from tools.translate import _ + +PURCHASE_ORDER_LINE_EDI_STRUCT = { + 'name': True, + 'date_planned': True, + 'product_id': True, + 'product_uom': True, + 'price_unit': True, + 'product_qty': True, + 'notes': True, +} + +PURCHASE_ORDER_EDI_STRUCT = { + 'company_id': True, # -> to be changed into partner + 'name': True, + 'partner_ref': True, + 'origin': True, + 'date_order': True, + 'partner_id': True, + #custom: 'partner_address', + 'notes': True, + 'order_line': PURCHASE_ORDER_LINE_EDI_STRUCT, + #custom: currency_id +} + +class purchase_order(osv.osv, EDIMixin): + _inherit = 'purchase.order' + + def edi_export(self, cr, uid, records, edi_struct=None, context=None): + """Exports a purchase order""" + edi_struct = dict(edi_struct or PURCHASE_ORDER_EDI_STRUCT) + res_company = self.pool.get('res.company') + res_partner_address = self.pool.get('res.partner.address') + edi_doc_list = [] + for order in records: + # Get EDI doc based on struct. The result will also contain all metadata fields and attachments. + edi_doc = super(purchase_order,self).edi_export(cr, uid, [order], edi_struct, context)[0] + edi_doc.update({ + # force trans-typing to purchase.order upon import + '__import_model': 'sale', + '__import_module': 'sale', + + 'company_address': res_company.edi_export_address(cr, uid, order.company_id, context=context), + 'company_paypal_account': order.company_id.paypal_account, + 'partner_address': res_partner_address.edi_export(cr, uid, [order.partner_address_id], context=context)[0], + + 'currency_id': self.edi_m2o(cr, uid, order.pricelist_id.currency_id, context=context), + #'company_logo': #TODO + }) + edi_doc_list.append(edi_doc) + return edi_doc_list + + def edi_import_company(self, cr, uid, edi_document, context=None): + # TODO: for multi-company setups, we currently import the document in the + # user's current company, but we should perhaps foresee a way to select + # the desired company among the user's allowed companies + + self._edi_requires_attributes(('company_id','company_address'), edi_document) + res_partner_address = self.pool.get('res.partner.address') + res_partner = self.pool.get('res.partner') + + # imported company = as a 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) + partner_value = {'customer': 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'] = 'default' + address_id = res_partner_address.edi_import(cr, uid, address_info, context=context) + + # modify edi_document to refer to new partner/address + 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['partner_address_id'] = self.edi_m2o(cr, uid, partner_address, context=context) + + return partner_id + + def _edi_get_pricelist(self, cr, uid, partner_id, currency, context=None): + # TODO: refactor into common place for purchase/sale, e.g. into product module + partner_model = self.pool.get('res.partner') + partner = partner_model.browse(cr, uid, partner_id, context=context) + pricelist = partner.property_product_pricelist_purchase + if not pricelist: + pricelist = self.pool.get('ir.model.data').get_object(cr, uid, 'purchase', 'list0', context=context) + + if not pricelist.currency_id == currency: + # look for a pricelist with the right type and currency, or make a new one + pricelist_type = 'purchase' + product_pricelist = self.pool.get('product.pricelist') + match_pricelist_ids = product_pricelist.search(cr, uid,[('type','=',pricelist_type), + ('currency_id','=',currency.id)]) + if match_pricelist_ids: + pricelist_id = match_pricelist_ids[0] + else: + pricelist_name = _('EDI Pricelist (%s)') % (currency.name,) + pricelist_id = product_pricelist.create(cr, uid, {'name': pricelist_name, + 'type': pricelist_type, + 'currency_id': currency.id, + }) + self.pool.get('product.pricelist.version').create(cr, uid, {'name': pricelist_name, + 'pricelist_id': pricelist_id}) + pricelist = product_pricelist.browse(cr, uid, pricelist_id) + + return self.edi_m2o(cr, uid, pricelist, context=context) + + def _edi_get_location(self, cr, uid, partner_id, context=None): + partner_model = self.pool.get('res.partner') + partner = partner_model.browse(cr, uid, partner_id, context=context) + location = partner.property_stock_customer + if not location: + location = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'stock_location_stock', context=context) + return self.edi_m2o(cr, uid, location, context=context) + + def edi_import(self, cr, uid, edi_document, context=None): + self._edi_requires_attributes(('company_id','company_address','order_line','date_order','currency_id'), edi_document) + + #import company as a new partner + partner_id = self.edi_import_company(cr, uid, edi_document, context=context) + + # currency for rounding the discount calculations and for the pricelist + currency_id, currency_name = edi_document.pop('currency_id') + currency_id = self.edi_import_relation(cr, uid, 'res.currency', currency_name, currency_id, context=context) + res_currency = self.pool.get('res.currency') + order_currency = res_currency.browse(cr, uid, currency_id) + + partner_ref = edi_document.pop('partner_ref', False) + edi_document['partner_ref'] = edi_document['name'] + edi_document['name'] = partner_ref or edi_document['name'] + edi_document['pricelist_id'] = self._edi_get_pricelist(cr, uid, partner_id, order_currency, context=context) + edi_document['location_id'] = self._edi_get_location(cr, uid, partner_id, context=context) + + for order_line in edi_document['order_line']: + self._edi_requires_attributes(('date_planned', 'product_id', 'product_uom', 'product_qty', 'price_unit'), order_line) + # original sale order contains unit price and discount, but not final line price + discount = order_line.pop('discount', 0.0) + if discount: + order_line['price_unit'] = res_currency.round(cr, uid, order_currency, + (order_line['price_unit'] * (1 - (discount or 0.0) / 100.0))) + # sale order lines have sequence numbers, not purchase order lines + order_line.pop('sequence', None) + + return super(purchase_order,self).edi_import(cr, uid, edi_document, context=context) + +class purchase_order_line(osv.osv, EDIMixin): + _inherit='purchase.order.line' + + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/addons/purchase/edi/purchase_order_action_data.xml b/addons/purchase/edi/purchase_order_action_data.xml new file mode 100644 index 00000000000..68e95dbad66 --- /dev/null +++ b/addons/purchase/edi/purchase_order_action_data.xml @@ -0,0 +1,169 @@ + + + + + + ") + + # accessing the template will fail if it has been deleted - so we'll skip everything + tmpl = self.pool.get('ir.model.data').get_object(cr, uid, 'purchase', 'email_template_edi_purchase') + + edi_token = self.pool.get('edi.document').export_edi(cr, uid, [object], context = context)[0] + context.update(edi_web_url_view='%s/web/view_edi?db=%s&token=%s' %(web_root_url, cr.dbname, edi_token)) + self.pool.get('email.template').send_mail(cr, uid,tmpl.id,object.id,context=context) +except: + pass + ]]> + code + ir.actions.server + + True + Auto-email confirmed purchase orders + + + + + + + + + + + + + Automated Purchase Order Notification Mail + ${object.validator.user_email or ''} + ${object.company_id.name} Order (Ref ${object.name or 'n/a' }) + ${object.partner_address_id.email} + + + +

Hello${object.partner_address_id.name and ' ' or ''}${object.partner_address_id.name or ''},

+ +

Your order for ${object.partner_id.name} has just been validated:

+ +

+   REFERENCES
+   Order number: ${object.name}
+   Order total: ${object.amount_total} ${object.pricelist_id.currency_id.name}
+   Order date: ${object.date_order}
+ % if object.origin: +   Order reference: ${object.origin}
+ % endif + % if object.partner_ref: +   Your reference: ${object.partner_ref}
+ % endif +   Your contact: ${object.validator.name} +

+ +

+ You can view the order confirmation document, download it and pay online using the following link: +

+ View Order + + % if object.company_id.paypal_account: +
+

It is also possible to directly pay with Paypal:

+ Pay with Paypal + % endif + +
+

If you have any question, do not hesitate to contact us.

+

Thank you for choosing ${object.company_id.name or 'us'}!

+
+
+
+

+ ${object.company_id.name}

+
+
+ + % if object.company_id.street: + ${object.company_id.street}
+ % 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} ${object.company_id.city}
+ % 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: + + %endif +

+
+
+ ]]>
+ + % endif + | Your contact: ${object.validator.name} ${object.validator.user_email and '<%s>'%(object.validator.user_email) or ''} + +You can view the order confirmation, download it and even pay online using the following link: + ${ctx.get('edi_web_url_view') or 'n/a'} + +% if object.company_id.paypal_account: +It is also possible to directly pay with Paypal: + ${"https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Order%%20%s&invoice=%s&amount=%s¤cy_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Order_PayNow_%s"%(object.company_id.paypal_account,object.name and object.name.replace('/','%2f') or '', object.name and object.name.replace('/','%2f') or '', object.amount_total, object.pricelist_id.currency_id.name, object.pricelist_id.currency_id.name)} +% endif + +If you have any question, do not hesitate to contact us. + + +Thank you for choosing ${object.company_id.name}! + + +-- +${object.validator.name} ${object.validator.user_email and '<%s>'%(object.validator.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 + ]]> +
+
+ \ No newline at end of file diff --git a/addons/purchase/edi_purchase_order.py b/addons/purchase/edi_purchase_order.py deleted file mode 100644 index 9119485bffc..00000000000 --- a/addons/purchase/edi_purchase_order.py +++ /dev/null @@ -1,157 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2009 Tiny SPRL (). -# -# 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 . -# -############################################################################## - -from osv import fields, osv, orm -from base.ir import ir_edi -from tools.translate import _ -from datetime import date, datetime, timedelta - -class purchase_order(osv.osv, ir_edi.edi): - _inherit = 'purchase.order' - - def edi_export(self, cr, uid, records, edi_struct=None, context=None): - """Exports a purchase order""" - edi_struct = { - 'company_id': True, # -> to be changed into partner - 'name': True, - 'partner_ref': True, - 'origin': True, - 'date_order': True, - 'partner_id': True, - 'partner_address_id': True, #only one address needed - #SO: 'partner_order_id' - #PO: 'partner_address_id' - - 'pricelist_id': True, - 'notes': True, - #SO: 'note' - #PO: 'notes' - - 'amount_total': True, - 'amount_tax': True, - 'amount_untaxed': True, - 'order_line': { - 'name': True, - 'date_planned': True, - #SO: 'delay' : 'date_order' - 'date_planned' - #PO: 'date_planned': 'date_order' + 'delay' - - 'product_id': True, - 'product_uom': True, - 'price_unit': True, - 'price_subtotal': True, - 'product_qty': True, - #SO: 'product_uom_qty' - #PO: 'product_qty' - - 'notes': True, - } - } - company_pool = self.pool.get('res.company') - edi_doc_list = [] - for order in records: - # Get EDI doc based on struct. The result will also contain all metadata fields and attachments. - edi_doc = super(purchase_order,self).edi_export(cr, uid, [order], edi_struct, context) - if not edi_doc: - continue - edi_doc = edi_doc[0] - currency = order.company_id.currency_id - - # Add company info and address - edi_company_document = company_pool.edi_export_address(cr, uid, [order.company_id], context=context)[order.company_id.id] - edi_doc.update({ - 'company_address': edi_company_document['company_address'], - 'currency_id': currency and self.edi_m2o(cr, uid, currency, context=context) or False, - #'company_logo': edi_company_document['company_logo'],#TODO - }) - edi_doc_list.append(edi_doc) - return edi_doc_list - - def edi_import_company(self, cr, uid, edi_document, context=None): - partner_address_pool = self.pool.get('res.partner.address') - partner_pool = self.pool.get('res.partner') - company_pool = self.pool.get('res.company') - - # import company as a new partner, supplier=1. - # company_address data used to add address to new partner - partner_value = {'customer': True} - partner_id = company_pool.edi_import_as_partner(cr, uid, edi_document, values=partner_value, context=context) - - - # partner_id field is modified to point to the new partner - res = partner_pool.address_get(cr, uid, [partner_id], ['contact', 'invoice']) - address_id = res['invoice'] - partner = partner_pool.browse(cr, uid, partner_id, context=context) - partner_address = partner_address_pool.browse(cr, uid, address_id, context=context) - edi_document['partner_id'] = self.edi_m2o(cr, uid, partner, context=context) - edi_document['partner_address_id'] = self.edi_m2o(cr, uid, partner_address, context=context) - del edi_document['company_id'] - return partner_id - - def edi_get_pricelist(self, cr, uid, partner_id, context=None): - # return value = ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.list0", "Public Pricelist (EUR)"] - partner_model = self.pool.get('res.partner') - partner = partner_model.browse(cr, uid, partner_id, context=context) - pricelist = partner.property_product_pricelist_purchase - if not pricelist: - pricelist = self.pool.get('ir.model.data').get_object(cr, uid, 'purchase', 'list0', context=context) - return self.edi_m2o(cr, uid, pricelist, context=context) - - def edi_get_location(self, cr, uid, partner_id, context=None): - # return value = ["724f93ec-ddd0-11e0-88ec-701a04e25543:stock.stock_location_stock", "Stock"] - partner_model = self.pool.get('res.partner') - partner = partner_model.browse(cr, uid, partner_id, context=context) - location = partner.property_stock_customer - if not location: - location = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'stock_location_stock', context=context) - return self.edi_m2o(cr, uid, location, context=context) - - def edi_import(self, cr, uid, edi_document, context=None): - if context is None: - context = {} - - #import company as a new partner - partner_id = self.edi_import_company(cr, uid, edi_document, context=context) - - edi_document['partner_ref'] = edi_document['name'] - edi_document['notes'] = edi_document.get('note', False) - edi_document['pricelist_id'] = self.edi_get_pricelist(cr, uid, partner_id, context=context) - edi_document['location_id'] = self.edi_get_location(cr, uid, partner_id, context=context) - for order_line in edi_document['order_line']: - order_line['product_qty'] = order_line['product_uom_qty'] - date_order = datetime.strptime(edi_document['date_order'], "%Y-%m-%d") - delay = order_line.get('delay', 0.0) - order_line['date_planned'] = (date_order + timedelta(days=delay)).strftime("%Y-%m-%d") - # price_unit = price_unit - discount - discount = order_line.get('discount', 0.0) - price_unit = order_line['price_unit'] - if discount: - price_unit = price_unit * (1 - (discount or 0.0) / 100.0) - order_line['price_unit'] = price_unit - - return super(purchase_order,self).edi_import(cr, uid, edi_document, context=context) -purchase_order() - -class purchase_order_line(osv.osv, ir_edi.edi): - _inherit='purchase.order.line' - -purchase_order_line() -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/purchase/edi_purchase_order_data.xml b/addons/purchase/edi_purchase_order_data.xml deleted file mode 100644 index 02ecb24b9ba..00000000000 --- a/addons/purchase/edi_purchase_order_data.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - context.update({'edi_web_purchase_url_view': '%s/web/view_edi?db=%s&token=%s' %(self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'),cr.dbname, self.pool.get('ir.edi.document').export_edi(cr, uid, [object], context = context)[0])}) -if not object.partner_id.opt_out: self.pool.get('email.template').send_mail(cr, - uid, - self.pool.get('ir.model.data').get_object_reference(cr, uid, 'purchase', 'email_template_edi_purchase')[1], - object.id, - context=context) - - code - ir.actions.server - - True - EDI Document - Purchase Order - - - - - - - - - - - - Mail Template of Purchase Order For EDI Document - ${object.validator.user_email or ''} - ${object.validator.user_email or ''} - ${object.company_id.name} - Purchase Order ${object.name} - ${object.partner_address_id.email} - - -<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); "> - -<p> Hello ${object.partner_address_id.name and ' ' or ''},</p> -<p> You can click on the following link to preview purchase order: <br/> - <a href="${object._context.get('edi_web_purchase_url_view')}">${object._context.get('edi_web_purchase_url_view')} </a> -</p> - - -<p style="border-left: 1px solid #8e0000; margin-left: 30px;"> <strong>REFERENCES</strong><br /> Order number: <strong>${object.name}</strong><br /> Order amount: <strong>${object.amount_total} </strong><br /> Order date: ${object.date_order or 'n/a'}<br /> Your contact: <a href="mailto:${object.validator.user_email or ''}?subject=Order%20${object.name}">${object.validator.name}</a></p> - - -<p> If you have any question, do not hesitate to reply directly to this e-mail.</p> <p> Thank you for choosing OpenERP!<br /> </p> <div style="width: 375px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; overflow-x: hidden; overflow-y: hidden; zoom: 1; background-image: url(http://www.openerp.com/sites/default/files/red_gradient_bg.png); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(142, 0, 0); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-bottom-right-radius: 0px 0px; border-bottom-left-radius: 0px 0px; background-position: 0% 0%; background-repeat: repeat no-repeat; "> <h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 9px; padding-right: 14px; padding-bottom: 9px; padding-left: 14px; font-size: 12px; font-weight: normal; font-style: normal; color: rgb(255, 255, 255); "> <strong>${object.company_id.name}</strong></h3> </div> <div style="width: 347px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 12px; padding-right: 14px; padding-bottom: 12px; padding-left: 14px; overflow-x: hidden; overflow-y: hidden; zoom: 1; line-height: 16px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(242, 242, 242); "> <div> Contact:<a href="mailto:${object.validator.user_email or ''}?subject=Order%20${object.name}">${object.validator.name}</a></div> <div> </div> </div> </div> <p> </p> - - -Hello ${object.partner_address_id.name and ' ' or ''}, - -You can click on the following link to preview Purchase Order: - - ${object._context.get('edi_web_purchase_url_view') or 'n/a'} - -Order Number: *${object.name}* -Amount: *${object.amount_total}* -Order date: ${object.date_order or 'n/a'} -Your contact: ${object.validator.name} ${object.validator.user_email and '<%s>'%(object.validator.user_email) or ''} - - -If you have any question, do not hesitate to reply directly to this e-mail. - -Thank you for choosing our service! - --- -${object.company_id.name} -Contact: ${object.validator.name} ${object.validator.user_email and '<%s>'%(object.validator.user_email) or ''} - - - - - diff --git a/addons/purchase/test/edi_purchase_order.yml b/addons/purchase/test/edi_purchase_order.yml index a5d06329bb5..68519fd0313 100644 --- a/addons/purchase/test/edi_purchase_order.yml +++ b/addons/purchase/test/edi_purchase_order.yml @@ -1,17 +1,9 @@ - - I create a partner which is a my customer + I create a draft Purchase Order - - !record {model: res.partner, id: res_partner_test20}: - name: jones white - supplier: False - customer: True - opt_out: True -- - I create one Purchase Order -- - !record {model: purchase.order, id: purchase_order_test}: - partner_id: res_partner_test20 - partner_address_id: base.res_partner_address_11 + !record {model: purchase.order, id: purchase_order_edi_1}: + partner_id: base.res_partner_agrolait + partner_address_id: base.res_partner_address_8invoice location_id: stock.stock_location_3 pricelist_id: 1 order_line: @@ -19,108 +11,111 @@ product_qty: 1.0 product_uom: 1 price_unit: 150.0 - name: 'basic pc' + name: 'Basic PC' date_planned: '2011-08-31' order_line: - product_id: product.product_product_pc3 product_qty: 10.0 product_uom: 1 price_unit: 20.0 - name: 'medium pc' + name: 'Medium PC' date_planned: '2011-08-31' + - - I Open the Purchase order + I confirm the purchase order - - !python {model: purchase.order}: | - - orders = self.browse(cr, uid, ref("purchase_order_test")) - import netsvc - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'purchase.order',orders.id,'approved', cr) + !workflow {model: purchase.order, ref: purchase_order_edi_1, action: purchase_confirm} - - I Tesing of EDI functionality. First I export Purchase Order from my company than import that Order into customer company + Then I export the purchase order via EDI - - !python {model: ir.edi.document}: | + !python {model: edi.document}: | order_pool = self.pool.get('purchase.order') - order = order_pool.browse(cr, uid, ref("purchase_order_test")) - - tokens = self.export_edi(cr, uid, [order]) - assert tokens, 'Token is not generated' + order = order_pool.browse(cr, uid, ref("purchase_order_edi_1")) + token = self.export_edi(cr, uid, [order]) + assert token, 'Invalid EDI Token' - - I import purchase order from EDI Document of sale order + Then I import a sample EDI document of a sale order - - !python {model: ir.edi.document}: | + !python {model: edi.document}: | purchase_order_pool = self.pool.get('purchase.order') edi_document = { - "order_line": [{ - "product_uom_qty": 1.0, - "name": "basic pc", - "product_uom": ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.product_uom_unit", "PCE"], - "sequence": 10, - "price_unit": 150.0, - "__last_update": False, - "__id": "724f93ec-ddd0-11e0-88ec-701a04e25543:724f93ec-ddd0-11e0-88ec-701a04e25543:sale.sale_order_line-LXEqeuI-SSP0", - "product_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.product_product_pc1", "[PC1] Basic PC"] - }, - { - "product_uom_qty": 10.0, - "name": "medium pc", - "product_uom": ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.product_uom_unit", "PCE"], - "sequence": 11, - "price_unit": 20.0, - "__last_update": False, - "__id": "724f93ec-ddd0-11e0-88ec-701a04e25543:724f93ec-ddd0-11e0-88ec-701a04e25543:sale.sale_order_line-LXEqeuI-SSP0", - "product_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.product_product_pc3", "[PC3] Medim PC"] - }], - "order_policy": "manual", - "company_address": { + "__id": "sale:724f93ec-ddd0-11e0-88ec-701a04e25543.sale_order_test", + "__module": "sale", + "__model": "sale.order", + "__import_module": "purchase", + "__import_model": "purchase.order", + "__version": [6,1,0], + "name": "SO008", + "currency_id": ["base:724f93ec-ddd0-11e0-88ec-701a04e25543.EUR", "EUR (€)"], + "date_order": "2011-09-13", + "partner_id": ["sale:724f93ec-ddd0-11e0-88ec-701a04e25543.res_partner_test22", "Junjun wala"], + "partner_address": { + "__id": "base:724f93ec-ddd0-11e0-88ec-701a04e25543.res_partner_address_7wdsjasdjh", + "__module": "base", + "__model": "res.partner.address", + "phone": "(+32).81.81.37.00", + "street": "Chaussee de Namur 40", "city": "Gerompont", "zip": "1367", - "__last_update": False, - "country_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:base.be", "Belgium"], - "__id": "724f93ec-ddd0-11e0-88ec-701a04e25543:base.main_address", + "country_id": ["base:5af1272e-dd26-11e0-b65e-701a04e25543.be", "Belgium"], + }, + "company_id": ["base:724f93ec-ddd0-11e0-88ec-701a04e25543.main_company", "Supplier S.A."], + "company_address": { + "__id": "base:724f93ec-ddd0-11e0-88ec-701a04e25543.main_address", + "__module": "base", + "__model": "res.partner.address", + "city": "Gerompont", + "zip": "1367", + "country_id": ["base:724f93ec-ddd0-11e0-88ec-701a04e25543.be", "Belgium"], "phone": "(+32).81.81.37.00", - "street": "Chaussee de Namur 40" + "street": "Chaussee de Namur 40", + "street2": "mailbox 34", }, - "partner_order_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:base.res_partner_address_3", "Thomas Passot, Belgium, Louvain-la-Neuve, Rue de l'Angelique, 1"], - "__id": "724f93ec-ddd0-11e0-88ec-701a04e25543:sale.sale_order_test", - "date_order": "2011-09-13", - "partner_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:sale.res_partner_test22", "Junjun wala"], - "__attachments": [], - "__module": "purchase", - "amount_total": 350.0, - "amount_untaxed": 350.0, - "name": "SO008", - "__model": "purchase.order", - "__last_update": False, - "company_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:base.main_company", "Supplier S.A."], - "__version": [6, 1], - "pricelist_id": ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.list0", "Public Pricelist (EUR)"] + "order_line": [{ + "__id": "sale:724f93ec-ddd0-11e0-88ec-701a04e25543.sale_order_line-LXEqeuI-SSP0", + "__module": "sale", + "__model": "sale.order.line", + "__import_module": "purchase", + "__import_model": "purchase.order.line", + "name": "Basic PC", + "product_uom": ["product:724f93ec-ddd0-11e0-88ec-701a04e25543.product_uom_unit", "PCE"], + "product_qty": 1.0, + "date_planned": "2011-09-30", + "sequence": 10, + "price_unit": 150.0, + "product_id": ["product:724f93ec-ddd0-11e0-88ec-701a04e25543.product_product_pc1", "[PC1] Basic PC"], + }, + { + "__id": "sale:724f93ec-ddd0-11e0-88ec-701a04e25543.sale_order_line-LXEqeadasdad", + "__module": "sale", + "__model": "sale.order.line", + "__import_module": "purchase", + "__import_model": "purchase.order.line", + "name": "Medium PC", + "product_uom": ["product:724f93ec-ddd0-11e0-88ec-701a04e25543.product_uom_unit", "PCE"], + "product_qty": 10.0, + "sequence": 11, + "date_planned": "2011-09-15", + "price_unit": 20.0, + "product_id": ["product:724f93ec-ddd0-11e0-88ec-701a04e25543.product_product_pc3", "[PC3] Medium PC"], + }], } new_purchase_order_id = purchase_order_pool.edi_import(cr, uid, edi_document, context=context) - assert new_purchase_order_id, 'Purchase order is not imported' - -- - I Checking the sale order become purchase order or not after import -- - !python {model: purchase.order}: | - - ids = self.search(cr, uid, [('partner_id','=','Supplier S.A.'), ('partner_ref', '=', 'SO008')]) - assert ids, 'Order does not have created of party' - order_new = self.browse(cr, uid, ids[0]) - assert order_new.pricelist_id.name == 'Public Pricelist' , "Public Price list is not imported" + assert new_purchase_order_id, 'Purchase order import failed' + order_new = purchase_order_pool.browse(cr, uid, new_purchase_order_id) + assert order_new.pricelist_id.name == 'Default Purchase Pricelist' , "Default Purchase Pricelist was not automatically assigned" assert order_new.amount_total == 350, "Amount total is not same" assert order_new.amount_untaxed == 350, "untaxed amount is not same" - assert len(order_new.order_line) == 2, "Purchase lines are not same" + assert len(order_new.order_line) == 2, "Purchase order lines number mismatch" for purchase_line in order_new.order_line: - if purchase_line.name == 'basic pc': + if purchase_line.name == 'Basic PC': assert purchase_line.product_uom.name == "PCE" , "uom is not same" - assert purchase_line.price_unit == 150 , "price unit is not same" + assert purchase_line.price_unit == 150 , "unit price is not same, got %s, expected 150"%(purchase_line.price_unit,) assert purchase_line.product_qty == 1 , "product qty is not same" - elif purchase_line.name == 'medium pc': + elif purchase_line.name == 'Medium PC': assert purchase_line.product_uom.name == "PCE" , "uom is not same" - assert purchase_line.price_unit == 20 , "price unit is not same" + assert purchase_line.price_unit == 20 , "unit price is not same, got %s, expected 20"%(purchase_line.price_unit,) assert purchase_line.product_qty == 10 , "product qty is not same" else: - assert 'wrong product imported in purchase lines' + raise AssertionError('unknown order line: %s' % purchase_line) From ba59e7d618fe17cd55750ee6f874b4090fd12293 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 6 Oct 2011 18:35:09 +0200 Subject: [PATCH 068/132] [IMP] sale: edi - better handling of currencies and pricelists bzr revid: odo@openerp.com-20111006163509-tu3s8tb4ss6hfdl4 --- addons/sale/edi/sale_order.py | 48 +++++++++++++++++----- addons/sale/edi/sale_order_action_data.xml | 1 - addons/sale/test/edi_sale_order.yml | 1 + 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/addons/sale/edi/sale_order.py b/addons/sale/edi/sale_order.py index 958dd1f70ec..d262cbb9255 100644 --- a/addons/sale/edi/sale_order.py +++ b/addons/sale/edi/sale_order.py @@ -28,17 +28,13 @@ from tools import DEFAULT_SERVER_DATE_FORMAT SALE_ORDER_LINE_EDI_STRUCT = { 'sequence': True, - #SO: yes - #PO: No 'name': True, - #custom: 'planned_date' + #custom: 'date_planned' 'product_id': True, 'product_uom': True, 'price_unit': True, #custom: 'product_qty' 'discount': True, - #SO: yes - #PO: No 'notes': True, } @@ -75,7 +71,7 @@ class sale_order(osv.osv, EDIMixin): 'company_paypal_account': order.company_id.paypal_account, 'partner_address': res_partner_address.edi_export(cr, uid, [order.partner_order_id], context=context)[0], - 'currency_id': self.edi_m2o(cr, uid, order.company_id.currency_id, context=context), + 'currency_id': self.edi_m2o(cr, uid, order.pricelist_id.currency_id, context=context), 'partner_ref': order.client_order_ref or False, 'notes': order.note or False, #TODO: company_logo @@ -117,27 +113,55 @@ class sale_order(osv.osv, EDIMixin): return partner_id - def edi_get_pricelist(self, cr, uid, partner_id, context=None): - # value = ["724f93ec-ddd0-11e0-88ec-701a04e25543:product.list0", "Public Pricelist (EUR)"] + def _edi_get_pricelist(self, cr, uid, partner_id, currency, context=None): + # TODO: refactor into common place for purchase/sale, e.g. into product module partner_model = self.pool.get('res.partner') partner = partner_model.browse(cr, uid, partner_id, context=context) pricelist = partner.property_product_pricelist if not pricelist: pricelist = self.pool.get('ir.model.data').get_object(cr, uid, 'product', 'list0', context=context) + + if not pricelist.currency_id == currency: + # look for a pricelist with the right type and currency, or make a new one + pricelist_type = 'sale' + product_pricelist = self.pool.get('product.pricelist') + match_pricelist_ids = product_pricelist.search(cr, uid,[('type','=',pricelist_type), + ('currency_id','=',currency.id)]) + if match_pricelist_ids: + pricelist_id = match_pricelist_ids[0] + else: + pricelist_name = _('EDI Pricelist (%s)') % (currency.name,) + pricelist_id = product_pricelist.create(cr, uid, {'name': pricelist_name, + 'type': pricelist_type, + 'currency_id': currency.id, + }) + self.pool.get('product.pricelist.version').create(cr, uid, {'name': pricelist_name, + 'pricelist_id': pricelist_id}) + pricelist = product_pricelist.browse(cr, uid, pricelist_id) + return self.edi_m2o(cr, uid, pricelist, context=context) def edi_import(self, cr, uid, edi_document, context=None): - self._edi_requires_attributes(('company_id','company_address','order_line','date_order'), edi_document) + self._edi_requires_attributes(('company_id','company_address','order_line','date_order','currency_id'), edi_document) #import company as a new partner partner_id = self._edi_import_company(cr, uid, edi_document, context=context) + # currency for rounding the discount calculations and for the pricelist + currency_id, currency_name = edi_document.pop('currency_id') + currency_id = self.edi_import_relation(cr, uid, 'res.currency', currency_name, currency_id, context=context) + res_currency = self.pool.get('res.currency') + order_currency = res_currency.browse(cr, uid, currency_id) + date_order = edi_document['date_order'] - edi_document['client_order_ref'] = edi_document.pop('partner_ref', False) + partner_ref = edi_document.pop('partner_ref', False) + edi_document['client_order_ref'] = edi_document['name'] + edi_document['name'] = partner_ref or edi_document['name'] edi_document['note'] = edi_document.pop('notes', False) - edi_document['pricelist_id'] = self.edi_get_pricelist(cr, uid, partner_id, context=context) + edi_document['pricelist_id'] = self._edi_get_pricelist(cr, uid, partner_id, order_currency, context=context) order_lines = edi_document['order_line'] for order_line in order_lines: + self._edi_requires_attributes(('date_planned', 'product_id', 'product_uom', 'product_qty', 'price_unit'), order_line) order_line['product_uom_qty'] = order_line['product_qty'] del order_line['product_qty'] date_planned = order_line.pop('date_planned') @@ -153,6 +177,8 @@ class sale_order_line(osv.osv, EDIMixin): _inherit='sale.order.line' def edi_export(self, cr, uid, records, edi_struct=None, context=None): + """Overridden to provides sale order line fields with the expected names + (sale and purchase orders have different column names)""" edi_struct = dict(edi_struct or SALE_ORDER_LINE_EDI_STRUCT) edi_doc_list = [] for line in records: diff --git a/addons/sale/edi/sale_order_action_data.xml b/addons/sale/edi/sale_order_action_data.xml index 6d46006f384..3d138e510aa 100644 --- a/addons/sale/edi/sale_order_action_data.xml +++ b/addons/sale/edi/sale_order_action_data.xml @@ -17,7 +17,6 @@ try: except: pass ]]>
- code ir.actions.server diff --git a/addons/sale/test/edi_sale_order.yml b/addons/sale/test/edi_sale_order.yml index d7a8f4806f3..353b3bf1690 100644 --- a/addons/sale/test/edi_sale_order.yml +++ b/addons/sale/test/edi_sale_order.yml @@ -45,6 +45,7 @@ "__version": [6,1,0], "name": "PO00011", "date_order": "2011-09-12", + "currency_id": ["base:5af1272e-dd26-11e0-b65e-701a04e25543.EUR", "EUR (€)"], "company_id": ["base:5af1272e-dd26-11e0-b65e-701a04e25543.main_company", "Client S.A."], "company_address": { "__id": "base:5af1272e-dd26-11e0-b65e-701a04e25543.some_address", From ae7de39d534757267a74a10444686b90cfba70c8 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Mon, 10 Oct 2011 12:36:37 +0200 Subject: [PATCH 069/132] [IMP] email_template: add urllib.quote in template rendering context bzr revid: odo@openerp.com-20111010103637-rev1ixeagcpxk34o --- addons/email_template/email_template.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index 3eb0c60cd41..eedeff8a96a 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -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'' From 1ab7b52db0a8f5bd4cd9abce6c4b7353d27e33e8 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Mon, 10 Oct 2011 12:39:04 +0200 Subject: [PATCH 070/132] [IMP] account,purchase,sale: proper URL quoting in auto-mail templates bzr revid: odo@openerp.com-20111010103904-cqn71nkh3h6rli9c --- addons/account/edi/invoice_action_data.xml | 25 +++++++++++++++--- .../edi/purchase_order_action_data.xml | 26 ++++++++++++++++--- addons/sale/edi/sale_order_action_data.xml | 26 ++++++++++++++++--- 3 files changed, 68 insertions(+), 9 deletions(-) diff --git a/addons/account/edi/invoice_action_data.xml b/addons/account/edi/invoice_action_data.xml index 1152d0bada5..eaf30f02b3d 100644 --- a/addons/account/edi/invoice_action_data.xml +++ b/addons/account/edi/invoice_action_data.xml @@ -66,11 +66,20 @@ except: href="${ctx.get('edi_web_url_view') or ''}">View Invoice % if object.company_id.paypal_account: + <% + comp_name = quote(object.company_id.name) + inv_number = quote(object.number) + paypal_account = quote(object.company_id.paypal_account) + inv_amount = quote(str(object.amount_total)) + cur_name = quote(object.currency_id.name) + paypal_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=%s%%20Invoice%%20%s&" \ + "invoice=%s&amount=%s&currency_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s" % \ + (paypal_account,comp_name,inv_number,inv_number,inv_amount,cur_name,cur_name) + %>

It is also possible to directly pay with Paypal:

Pay with Paypal + href="${paypal_url}">Pay with Paypal % endif
@@ -127,8 +136,18 @@ You can view the invoice document, download it and pay online using the followin ${ctx.get('edi_web_url_view') or 'n/a'} % if object.company_id.paypal_account: +<% +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: - ${"https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Invoice%%20%s&invoice=%s&amount=%s¤cy_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s"%(object.company_id.paypal_account, object.number and object.number.replace('/','%2f') or '', object.number and object.number.replace('/','%2f') or '', object.amount_total, object.currency_id.name, object.currency_id.name)} + ${paypal_url} % endif If you have any question, do not hesitate to contact us. diff --git a/addons/purchase/edi/purchase_order_action_data.xml b/addons/purchase/edi/purchase_order_action_data.xml index 68e95dbad66..cd18e024f21 100644 --- a/addons/purchase/edi/purchase_order_action_data.xml +++ b/addons/purchase/edi/purchase_order_action_data.xml @@ -68,11 +68,21 @@ except: href="${ctx.get('edi_web_url_view') or ''}">View Order % if object.company_id.paypal_account: + <% + comp_name = quote(object.company_id.name) + order_name = quote(object.name) + paypal_account = quote(object.company_id.paypal_account) + order_amount = quote(str(object.amount_total)) + cur_name = quote(object.pricelist_id.currency_id.name) + paypal_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=%s%%20Order%%20%s" \ + "&invoice=%s&amount=%s&currency_code=%s&button_subtype=services&no_note=1" \ + "&bn=OpenERP_Order_PayNow_%s" % \ + (paypal_account,comp_name,order_name,order_name,order_amount,cur_name,cur_name) + %>

It is also possible to directly pay with Paypal:

Pay with Paypal + href="${paypal_url}">Pay with Paypal % endif
@@ -132,8 +142,18 @@ You can view the order confirmation, download it and even pay online using the f ${ctx.get('edi_web_url_view') or 'n/a'} % if object.company_id.paypal_account: +<% +comp_name = quote(object.company_id.name) +order_name = quote(object.name) +paypal_account = quote(object.company_id.paypal_account) +order_amount = quote(str(object.amount_total)) +cur_name = quote(object.pricelist_id.currency_id.name) +paypal_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=%s%%20Order%%20%s&invoice=%s&amount=%s" \ + "¤cy_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Order_PayNow_%s" % \ + (paypal_account,comp_name,order_name,order_name,order_amount,cur_name,cur_name) +%> It is also possible to directly pay with Paypal: - ${"https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Order%%20%s&invoice=%s&amount=%s¤cy_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Order_PayNow_%s"%(object.company_id.paypal_account,object.name and object.name.replace('/','%2f') or '', object.name and object.name.replace('/','%2f') or '', object.amount_total, object.pricelist_id.currency_id.name, object.pricelist_id.currency_id.name)} + ${paypal_url} % endif If you have any question, do not hesitate to contact us. diff --git a/addons/sale/edi/sale_order_action_data.xml b/addons/sale/edi/sale_order_action_data.xml index 3d138e510aa..b256728f49b 100644 --- a/addons/sale/edi/sale_order_action_data.xml +++ b/addons/sale/edi/sale_order_action_data.xml @@ -70,11 +70,21 @@ except: href="${ctx.get('edi_web_url_view') or ''}">View Order % if object.company_id.paypal_account: + <% + comp_name = quote(object.company_id.name) + order_name = quote(object.name) + paypal_account = quote(object.company_id.paypal_account) + order_amount = quote(str(object.amount_total)) + cur_name = quote(object.pricelist_id.currency_id.name) + paypal_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=%s%%20Order%%20%s" \ + "&invoice=%s&amount=%s&currency_code=%s&button_subtype=services&no_note=1" \ + "&bn=OpenERP_Order_PayNow_%s" % \ + (paypal_account,comp_name,order_name,order_name,order_amount,cur_name,cur_name) + %>

It is also possible to directly pay with Paypal:

Pay with Paypal + href="${paypal_url}">Pay with Paypal % endif
@@ -134,8 +144,18 @@ You can view the order confirmation, download it and even pay online using the f ${ctx.get('edi_web_url_view') or 'n/a'} % if object.company_id.paypal_account: +<% +comp_name = quote(object.company_id.name) +order_name = quote(object.name) +paypal_account = quote(object.company_id.paypal_account) +order_amount = quote(str(object.amount_total)) +cur_name = quote(object.pricelist_id.currency_id.name) +paypal_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=%s%%20Order%%20%s&invoice=%s&amount=%s" \ + "¤cy_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Order_PayNow_%s" % \ + (paypal_account,comp_name,order_name,order_name,order_amount,cur_name,cur_name) +%> It is also possible to directly pay with Paypal: - ${"https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=OpenERP%%20Order%%20%s&invoice=%s&amount=%s¤cy_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Order_PayNow_%s"%(object.company_id.paypal_account,object.name and object.name.replace('/','%2f') or '', object.name and object.name.replace('/','%2f') or '', object.amount_total, object.pricelist_id.currency_id.name, object.pricelist_id.currency_id.name)} + ${paypal_url} % endif If you have any question, do not hesitate to contact us. From a5677f23b23d2580d35c63efa75a89c51ffcb081 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Mon, 10 Oct 2011 16:36:27 +0200 Subject: [PATCH 071/132] [IMP] edi: no need for XML markup escaping inside CDATA bzr revid: odo@openerp.com-20111010143627-dkxk2xab2phyob3g --- addons/account/edi/invoice_action_data.xml | 4 ++-- addons/purchase/edi/purchase_order_action_data.xml | 4 ++-- addons/sale/edi/sale_order_action_data.xml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/account/edi/invoice_action_data.xml b/addons/account/edi/invoice_action_data.xml index eaf30f02b3d..6d7ea3bffe6 100644 --- a/addons/account/edi/invoice_action_data.xml +++ b/addons/account/edi/invoice_action_data.xml @@ -130,7 +130,7 @@ A new invoice is available for ${object.partner_id.name}: % 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 ''} + | 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'} @@ -157,7 +157,7 @@ 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.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 ''} diff --git a/addons/purchase/edi/purchase_order_action_data.xml b/addons/purchase/edi/purchase_order_action_data.xml index cd18e024f21..740d0fe7491 100644 --- a/addons/purchase/edi/purchase_order_action_data.xml +++ b/addons/purchase/edi/purchase_order_action_data.xml @@ -136,7 +136,7 @@ Your order for ${object.partner_id.name} has just been validated: % if object.partner_ref: | Your reference: ${object.partner_ref}
% endif - | Your contact: ${object.validator.name} ${object.validator.user_email and '<%s>'%(object.validator.user_email) or ''} + | Your contact: ${object.validator.name} ${object.validator.user_email and '<%s>'%(object.validator.user_email) or ''} You can view the order confirmation, download it and even pay online using the following link: ${ctx.get('edi_web_url_view') or 'n/a'} @@ -163,7 +163,7 @@ Thank you for choosing ${object.company_id.name}! -- -${object.validator.name} ${object.validator.user_email and '<%s>'%(object.validator.user_email) or ''} +${object.validator.name} ${object.validator.user_email and '<%s>'%(object.validator.user_email) or ''} ${object.company_id.name} % if object.company_id.street: ${object.company_id.street or ''} diff --git a/addons/sale/edi/sale_order_action_data.xml b/addons/sale/edi/sale_order_action_data.xml index b256728f49b..691665f4919 100644 --- a/addons/sale/edi/sale_order_action_data.xml +++ b/addons/sale/edi/sale_order_action_data.xml @@ -138,7 +138,7 @@ Your order for ${object.partner_id.name} has just been validated: % if object.client_order_ref: | Your reference: ${object.client_order_ref}
% endif - | Your contact: ${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''} + | Your contact: ${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''} You can view the order confirmation, download it and even pay online using the following link: ${ctx.get('edi_web_url_view') or 'n/a'} @@ -165,7 +165,7 @@ 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.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 ''} From 1e80c28ef1fce2b37007d3ba6c9d5f0e0c74c0a3 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Mon, 10 Oct 2011 16:37:13 +0200 Subject: [PATCH 072/132] [IMP] edi: provide central metod to generate the `canonical` document printout for EDI export bzr revid: odo@openerp.com-20111010143713-jnr9tkkoz60m5pnj --- addons/edi/models/edi.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/addons/edi/models/edi.py b/addons/edi/models/edi.py index 6f8a7d32e31..636e5493589 100644 --- a/addons/edi/models/edi.py +++ b/addons/edi/models/edi.py @@ -29,9 +29,11 @@ import urllib2 import openerp import openerp.release as release +import netsvc from osv import osv,fields from tools.translate import _ from tools.parse_version import parse_version +from tools.safe_eval import safe_eval as eval EXTERNAL_ID_PATTERN = re.compile(r'^(\S+:)?(\S+?)\.(\S+)$') @@ -428,6 +430,33 @@ class EDIMixin(object): 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 = report.report_name+".printout.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', []): From b3276b708cafb91aa185617249dd674174d1b8f4 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Mon, 10 Oct 2011 16:38:06 +0200 Subject: [PATCH 073/132] [IMP] account,sale,purchase: edi - generate canonical report printout when exporting bzr revid: odo@openerp.com-20111010143806-wxxdoheg562mo8re --- addons/account/edi/invoice.py | 3 +++ addons/purchase/edi/purchase_order.py | 3 +++ addons/purchase/purchase_report.xml | 8 ++++++-- addons/sale/edi/sale_order.py | 5 ++++- addons/sale/sale_report.xml | 4 +++- 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/addons/account/edi/invoice.py b/addons/account/edi/invoice.py index 817bf583516..15fd1327db3 100644 --- a/addons/account/edi/invoice.py +++ b/addons/account/edi/invoice.py @@ -69,6 +69,9 @@ class account_invoice(osv.osv, EDIMixin): 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), diff --git a/addons/purchase/edi/purchase_order.py b/addons/purchase/edi/purchase_order.py index 4ac25adf23b..2bf823707a7 100644 --- a/addons/purchase/edi/purchase_order.py +++ b/addons/purchase/edi/purchase_order.py @@ -60,6 +60,9 @@ class purchase_order(osv.osv, EDIMixin): res_partner_address = self.pool.get('res.partner.address') edi_doc_list = [] for order in records: + # generate the main report + self._edi_generate_report_attachment(cr, uid, order, context=context) + # Get EDI doc based on struct. The result will also contain all metadata fields and attachments. edi_doc = super(purchase_order,self).edi_export(cr, uid, [order], edi_struct, context)[0] edi_doc.update({ diff --git a/addons/purchase/purchase_report.xml b/addons/purchase/purchase_report.xml index e13ca9911b0..a3a4a6a70b6 100644 --- a/addons/purchase/purchase_report.xml +++ b/addons/purchase/purchase_report.xml @@ -1,7 +1,11 @@ - - + + diff --git a/addons/sale/edi/sale_order.py b/addons/sale/edi/sale_order.py index d262cbb9255..d7ece92717a 100644 --- a/addons/sale/edi/sale_order.py +++ b/addons/sale/edi/sale_order.py @@ -60,6 +60,9 @@ class sale_order(osv.osv, EDIMixin): res_partner_address = self.pool.get('res.partner.address') edi_doc_list = [] for order in records: + # generate the main report + self._edi_generate_report_attachment(cr, uid, order, context=context) + # Get EDI doc based on struct. The result will also contain all metadata fields and attachments. edi_doc = super(sale_order,self).edi_export(cr, uid, [order], edi_struct, context)[0] edi_doc.update({ @@ -177,7 +180,7 @@ class sale_order_line(osv.osv, EDIMixin): _inherit='sale.order.line' def edi_export(self, cr, uid, records, edi_struct=None, context=None): - """Overridden to provides sale order line fields with the expected names + """Overridden to provide sale order line fields with the expected names (sale and purchase orders have different column names)""" edi_struct = dict(edi_struct or SALE_ORDER_LINE_EDI_STRUCT) edi_doc_list = [] diff --git a/addons/sale/sale_report.xml b/addons/sale/sale_report.xml index 33819df0bde..0f77913a3c9 100644 --- a/addons/sale/sale_report.xml +++ b/addons/sale/sale_report.xml @@ -2,7 +2,9 @@ - + From ad87e71d29ff8e9fca9a96c51b553c5ba1d79766 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 11 Oct 2011 10:54:52 +0200 Subject: [PATCH 074/132] [IMP] account,sale,purchase: edi - generate canonical report printout when exporting (second part) bzr revid: odo@openerp.com-20111011085452-klo63ng8b69mq7x1 --- addons/account/account_report.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/account/account_report.xml b/addons/account/account_report.xml index 2a22324d015..daa665e3ae3 100644 --- a/addons/account/account_report.xml +++ b/addons/account/account_report.xml @@ -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"/> From 25a5eee6fc7f13233051f489ec53511c46b9a751 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 13 Oct 2011 15:37:30 +0200 Subject: [PATCH 075/132] [FIX] edi: temptative fix for edi controllers (wip) bzr revid: odo@openerp.com-20111013133730-a9cr48vp9p55kjz6 --- addons/edi/controllers/edi_view.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/addons/edi/controllers/edi_view.py b/addons/edi/controllers/edi_view.py index c1137dcaf08..7805d9b11be 100644 --- a/addons/edi/controllers/edi_view.py +++ b/addons/edi/controllers/edi_view.py @@ -33,31 +33,30 @@ edi_view_template = textwrap.dedent(""" """) def edi_addons(): - _addons = ['web', 'web_edi'] + _addons = ['web', 'edi'] for addon in openerpweb.addons_module: if addon in _addons: continue manifest = openerpweb.addons_manifest.get(addon, {}) edi_addon = manifest.get('edi', False) - if edi_addon: _addons.append(addon) return _addons -class EDIView(openerpweb.Controller): +class EDIView(web.WebClient): # http://path.to.web.client:8080/web/view_edi?db=XXX&token=XXXXXXXXXXX _cp_path = "/web/view_edi" @openerpweb.httprequest def css(self, req): - files = web.manifest_glob(req.config.addons_path, edi_addons(), 'css') + files = self.manifest_glob(req, edi_addons(), 'css') content,timestamp = web.concat_files(req.config.addons_path, files) # TODO request set the Date of last modif and Etag return req.make_response(content, [('Content-Type', 'text/css')]) @openerpweb.httprequest def js(self, req): - files = web.manifest_glob(req.config.addons_path, edi_addons(), 'js') + files = self.manifest_glob(req, edi_addons(), 'js') content,timestamp = web.concat_files(req.config.addons_path, files) # TODO request set the Date of last modif and Etag return req.make_response(content, [('Content-Type', 'application/javascript')]) @@ -68,16 +67,16 @@ class EDIView(openerpweb.Controller): addons = edi_addons() jslist = ['/web/view_edi/js'] if req.debug: - jslist = [i + '?debug=' + str(time.time()) for i in web.manifest_glob(req.config.addons_path, addons, 'js')] + jslist = [i + '?debug=' + str(time.time()) for i in self.manifest_glob(req, addons, 'js')] js = "\n ".join([''%i for i in jslist]) # css tags csslist = ['/web/view_edi/css'] if req.debug: - csslist = [i + '?debug=' + str(time.time()) for i in web.manifest_glob(req.config.addons_path, addons, 'css')] + csslist = [i + '?debug=' + str(time.time()) for i in self.manifest_glob(req, addons, 'css')] css = "\n ".join([''%i for i in csslist]) - js_files = [js_file.split('/')[-1].split('.')[0] for js_file in web.manifest_glob(req.config.addons_path, edi_addons(), 'js')] + js_files = [str(js_file.split('/')[-1].split('.')[0]) for js_file in self.manifest_glob(req, addons, 'js')] r = edi_view_template % { 'javascript': js, From adbe3bf5ef01be78ee248f92c570f1a321357629 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 13 Oct 2011 16:59:12 +0200 Subject: [PATCH 076/132] [FIX] edi: temptative fix for edi controllers (wip) bzr revid: odo@openerp.com-20111013145912-zru7msgt9aodsdir --- addons/edi/controllers/edi_get.py | 9 ++++----- addons/edi/controllers/edi_import.py | 8 +------- addons/edi/controllers/edi_view.py | 9 ++++++--- addons/edi/edi_service.py | 18 +++++++++--------- addons/edi/static/src/js/edi_view.js | 8 ++++---- 5 files changed, 24 insertions(+), 28 deletions(-) diff --git a/addons/edi/controllers/edi_get.py b/addons/edi/controllers/edi_get.py index b53462b2a31..8d78a4d69b1 100644 --- a/addons/edi/controllers/edi_get.py +++ b/addons/edi/controllers/edi_get.py @@ -7,8 +7,8 @@ class EDIGet(openerpweb.Controller): _cp_path = "/web/get_edi" @openerpweb.httprequest - def index(self, req, token, db): - result = req.session.proxy('edi').get_edi_document(token, db) + def index(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))]) @@ -16,11 +16,10 @@ class EDIGet(openerpweb.Controller): return response @openerpweb.jsonrequest - def get_edi_document(self, req, token, db): - result = req.session.proxy('edi').get_edi_document(token, db) + def get_edi_document(self, req, db, token): + result = req.session.proxy('edi').get_edi_document(db, token) document = json.loads(result) model = document and document[0].get('__model') return {'token': token, 'db': db, 'model':model.replace('.','_'), 'document':document} - diff --git a/addons/edi/controllers/edi_import.py b/addons/edi/controllers/edi_import.py index a1d1c9496ef..ce3b9d57de7 100644 --- a/addons/edi/controllers/edi_import.py +++ b/addons/edi/controllers/edi_import.py @@ -4,8 +4,6 @@ import web.controllers.main as web import json import textwrap - - edi_import_template = textwrap.dedent(""" @@ -15,17 +13,13 @@ edi_import_template = textwrap.dedent(""" %(css)s %(javascript)s - + diff --git a/addons/edi/controllers/edi_view.py b/addons/edi/controllers/edi_view.py index 7805d9b11be..324bebed36a 100644 --- a/addons/edi/controllers/edi_view.py +++ b/addons/edi/controllers/edi_view.py @@ -33,7 +33,10 @@ edi_view_template = textwrap.dedent(""" """) def edi_addons(): - _addons = ['web', 'edi'] + #FIXME: hardcoded to be able to test + return 'web,edi,sale,purchase' + + _addons = ['web', 'edi', 'sale', 'purchase'] for addon in openerpweb.addons_module: if addon in _addons: continue @@ -50,14 +53,14 @@ class EDIView(web.WebClient): @openerpweb.httprequest def css(self, req): files = self.manifest_glob(req, edi_addons(), 'css') - content,timestamp = web.concat_files(req.config.addons_path, files) + content,timestamp = web.concat_files(req.config.addons_path[0], files) # TODO request set the Date of last modif and Etag return req.make_response(content, [('Content-Type', 'text/css')]) @openerpweb.httprequest def js(self, req): files = self.manifest_glob(req, edi_addons(), 'js') - content,timestamp = web.concat_files(req.config.addons_path, files) + content,timestamp = web.concat_files(req.config.addons_path[0], files) # TODO request set the Date of last modif and Etag return req.make_response(content, [('Content-Type', 'application/javascript')]) diff --git a/addons/edi/edi_service.py b/addons/edi/edi_service.py index 35b6d32e272..6d8a2e37036 100644 --- a/addons/edi/edi_service.py +++ b/addons/edi/edi_service.py @@ -31,13 +31,13 @@ class edi(netsvc.ExportService): netsvc.ExportService.__init__(self, name) def _edi_dispatch(self, db_name, method_name, *method_args): - registry = openerp.modules.registry.RegistryManager.get(db_name) - assert registry, 'Unknown database %s' % db_name - edi_document = registry['edi.document'] - cr = registry.db.cursor() 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) + 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) @@ -49,11 +49,11 @@ class edi(netsvc.ExportService): 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, uid, passwd, edi_document, context=None): - return self._edi_dispatch(db_name, 'import_edi', 1, edi_document, None) + 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, uid, passwd, edi_url, context=None): - return self._edi_dispatch(db_name, 'import_edi', 1, None, edi_url) + 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']: diff --git a/addons/edi/static/src/js/edi_view.js b/addons/edi/static/src/js/edi_view.js index 713e7d1fe9f..97642609f57 100644 --- a/addons/edi/static/src/js/edi_view.js +++ b/addons/edi/static/src/js/edi_view.js @@ -9,7 +9,7 @@ openerp.web.edi_view = function(openerp) { this.template = "EdiView"; this.element_id = element_id; this.$element = $('#' + element_id); - QWeb.add_template("/web_edi/static/src/xml/edi.xml"); + QWeb.add_template("/edi/static/src/xml/edi.xml"); }, start: function() { @@ -69,7 +69,7 @@ openerp.web.EdiViewCenter = openerp.web.Class.extend({ var self = this; this.edi_document = edi.document; this.$element = element; - QWeb.add_template("/web_edi/static/src/xml/edi.xml"); + QWeb.add_template("/edi/static/src/xml/edi.xml"); }, start: function() { }, @@ -85,7 +85,7 @@ openerp.web.EdiViewRightTop = openerp.web.Class.extend({ var self = this; this.edi_document = edi.document; this.$element = element; - QWeb.add_template("/web_edi/static/src/xml/edi.xml"); + QWeb.add_template("/edi/static/src/xml/edi.xml"); this.$_element = $('
') .appendTo(document.body) .delegate('button.oe_edi_button_import', 'click', {'edi': edi} , this.do_import) @@ -135,7 +135,7 @@ openerp.web.EdiViewRightBottom = openerp.web.Class.extend({ var self = this; this.edi_document = edi.document; this.$element = element; - QWeb.add_template("/web_edi/static/src/xml/edi.xml"); + QWeb.add_template("/edi/static/src/xml/edi.xml"); }, start: function() { }, From 2b59a0d6f4f85163b363294c535c942f937b9e73 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 13 Oct 2011 18:50:22 +0200 Subject: [PATCH 077/132] [FIX] edi: fix web paths bzr revid: odo@openerp.com-20111013165022-f21eqrzawwtg5oin --- addons/edi/controllers/edi_import.py | 4 ++-- addons/sale/static/src/js/edi_sale_purchase_order.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/edi/controllers/edi_import.py b/addons/edi/controllers/edi_import.py index ce3b9d57de7..462c9af94dc 100644 --- a/addons/edi/controllers/edi_import.py +++ b/addons/edi/controllers/edi_import.py @@ -38,13 +38,13 @@ class EDIImport(web.WebClient): def index(self, req, edi_url): # script tags addons = ['web'] - jslist = ['/web/webclient/js'] + jslist = ['/web/view_edi/js'] if req.debug: jslist = [i + '?debug=' + str(time.time()) for i in web.manifest_glob(req.config.addons_path, addons, 'js')] js = "\n ".join([''%i for i in jslist]) # css tags - csslist = ['/web/webclient/css'] + csslist = ['/web/view_edi/css'] if req.debug: csslist = [i + '?debug=' + str(time.time()) for i in web.manifest_glob(req.config.addons_path, addons, 'css')] css = "\n ".join([''%i for i in csslist]) diff --git a/addons/sale/static/src/js/edi_sale_purchase_order.js b/addons/sale/static/src/js/edi_sale_purchase_order.js index f0644c7407f..29c81dbe28d 100644 --- a/addons/sale/static/src/js/edi_sale_purchase_order.js +++ b/addons/sale/static/src/js/edi_sale_purchase_order.js @@ -8,7 +8,7 @@ openerp.web.EdiViewCenterSalePurchase = openerp.web.Class.extend({ var self = this; this.edi_document = eval(edi.document) this.$element = element; - QWeb.add_template("/web_edi_sale_purchase/static/src/xml/edi_sale_purchase_order.xml"); + QWeb.add_template("/sale/static/src/xml/edi_sale_purchase_order.xml"); }, start: function() { From a93ca01b5b1365f92e191e3c429ef655e751b455 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 13 Oct 2011 18:56:00 +0200 Subject: [PATCH 078/132] [FIX] sale,purchase: add totals to edi struct, used only for web preview bzr revid: odo@openerp.com-20111013165600-7k3dvkc406hoog0t --- addons/purchase/edi/purchase_order.py | 10 ++++++++++ addons/sale/edi/sale_order.py | 13 ++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/addons/purchase/edi/purchase_order.py b/addons/purchase/edi/purchase_order.py index 2bf823707a7..91c2d4ed60d 100644 --- a/addons/purchase/edi/purchase_order.py +++ b/addons/purchase/edi/purchase_order.py @@ -48,6 +48,11 @@ PURCHASE_ORDER_EDI_STRUCT = { 'notes': True, 'order_line': PURCHASE_ORDER_LINE_EDI_STRUCT, #custom: currency_id + + # fields used for web preview only - discarded on import + 'amount_total': True, + 'amount_untaxed': True, + 'amount_tax': True, } class purchase_order(osv.osv, EDIMixin): @@ -164,6 +169,11 @@ class purchase_order(osv.osv, EDIMixin): edi_document['pricelist_id'] = self._edi_get_pricelist(cr, uid, partner_id, order_currency, context=context) edi_document['location_id'] = self._edi_get_location(cr, uid, partner_id, context=context) + # discard web preview fields, if present + edi_document.pop('amount_total', None) + edi_document.pop('amount_tax', None) + edi_document.pop('amount_untaxed', None) + for order_line in edi_document['order_line']: self._edi_requires_attributes(('date_planned', 'product_id', 'product_uom', 'product_qty', 'price_unit'), order_line) # original sale order contains unit price and discount, but not final line price diff --git a/addons/sale/edi/sale_order.py b/addons/sale/edi/sale_order.py index d7ece92717a..5909eabd121 100644 --- a/addons/sale/edi/sale_order.py +++ b/addons/sale/edi/sale_order.py @@ -47,7 +47,12 @@ SALE_ORDER_EDI_STRUCT = { 'partner_id': True, #custom: 'partner_address' #custom: 'notes' - 'order_line': SALE_ORDER_LINE_EDI_STRUCT + 'order_line': SALE_ORDER_LINE_EDI_STRUCT, + + # fields used for web preview only - discarded on import + 'amount_total': True, + 'amount_untaxed': True, + 'amount_tax': True, } class sale_order(osv.osv, EDIMixin): @@ -162,6 +167,12 @@ class sale_order(osv.osv, EDIMixin): edi_document['name'] = partner_ref or edi_document['name'] edi_document['note'] = edi_document.pop('notes', False) edi_document['pricelist_id'] = self._edi_get_pricelist(cr, uid, partner_id, order_currency, context=context) + + # discard web preview fields, if present + edi_document.pop('amount_total', None) + edi_document.pop('amount_tax', None) + edi_document.pop('amount_untaxed', None) + order_lines = edi_document['order_line'] for order_line in order_lines: self._edi_requires_attributes(('date_planned', 'product_id', 'product_uom', 'product_qty', 'price_unit'), order_line) From fdc1eae69713de20aef404eb80c9857648106aba Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 13 Oct 2011 22:23:47 +0200 Subject: [PATCH 079/132] [IMP] account,sale,purchase: move all EDI web preview templates in edi module For technical reasons (web addons dependencies mgt), it is easier for now to keep all EDI preview templates in the edi module itself, rather than the individual business modules. Note: due to bzr bug, cannot bzr mv the files, had to hard-move them. bzr revid: odo@openerp.com-20111013202347-ry89xu8vm0y7mem0 --- addons/account/__openerp__.py | 3 --- addons/edi/__openerp__.py | 2 ++ addons/{account => edi}/static/src/js/edi_invoice.js | 0 addons/{sale => edi}/static/src/js/edi_sale_purchase_order.js | 2 +- addons/{account => edi}/static/src/xml/edi_invoice.xml | 0 .../{sale => edi}/static/src/xml/edi_sale_purchase_order.xml | 0 addons/purchase/__openerp__.py | 2 +- addons/sale/__openerp__.py | 3 --- 8 files changed, 4 insertions(+), 8 deletions(-) rename addons/{account => edi}/static/src/js/edi_invoice.js (100%) rename addons/{sale => edi}/static/src/js/edi_sale_purchase_order.js (90%) rename addons/{account => edi}/static/src/xml/edi_invoice.xml (100%) rename addons/{sale => edi}/static/src/xml/edi_sale_purchase_order.xml (100%) diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index 6aa096eedb7..008e7d78139 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -149,9 +149,6 @@ module named account_voucher. 'test/test_edi_invoice.yml', 'test/account_report.yml', ], - "js": [ - 'static/src/js/edi_invoice.js' - ], 'installable': True, 'active': False, 'certificate': '0080331923549', diff --git a/addons/edi/__openerp__.py b/addons/edi/__openerp__.py index b46a87ee9ca..60084c4c484 100644 --- a/addons/edi/__openerp__.py +++ b/addons/edi/__openerp__.py @@ -47,6 +47,8 @@ technical OpenERP documentation at http://doc.openerp.com 'static/src/js/sessionless.js', 'static/src/js/edi_import.js', 'static/src/js/edi_view.js', + 'static/src/js/edi_invoice.js', + 'static/src/js/edi_sale_purchase_order.js', ], 'installable': True, 'active': False, diff --git a/addons/account/static/src/js/edi_invoice.js b/addons/edi/static/src/js/edi_invoice.js similarity index 100% rename from addons/account/static/src/js/edi_invoice.js rename to addons/edi/static/src/js/edi_invoice.js diff --git a/addons/sale/static/src/js/edi_sale_purchase_order.js b/addons/edi/static/src/js/edi_sale_purchase_order.js similarity index 90% rename from addons/sale/static/src/js/edi_sale_purchase_order.js rename to addons/edi/static/src/js/edi_sale_purchase_order.js index 29c81dbe28d..1216957bf72 100644 --- a/addons/sale/static/src/js/edi_sale_purchase_order.js +++ b/addons/edi/static/src/js/edi_sale_purchase_order.js @@ -8,7 +8,7 @@ openerp.web.EdiViewCenterSalePurchase = openerp.web.Class.extend({ var self = this; this.edi_document = eval(edi.document) this.$element = element; - QWeb.add_template("/sale/static/src/xml/edi_sale_purchase_order.xml"); + QWeb.add_template("/edi/static/src/xml/edi_sale_purchase_order.xml"); }, start: function() { diff --git a/addons/account/static/src/xml/edi_invoice.xml b/addons/edi/static/src/xml/edi_invoice.xml similarity index 100% rename from addons/account/static/src/xml/edi_invoice.xml rename to addons/edi/static/src/xml/edi_invoice.xml diff --git a/addons/sale/static/src/xml/edi_sale_purchase_order.xml b/addons/edi/static/src/xml/edi_sale_purchase_order.xml similarity index 100% rename from addons/sale/static/src/xml/edi_sale_purchase_order.xml rename to addons/edi/static/src/xml/edi_sale_purchase_order.xml diff --git a/addons/purchase/__openerp__.py b/addons/purchase/__openerp__.py index 8e5b9803c6c..49d17d9e1e8 100644 --- a/addons/purchase/__openerp__.py +++ b/addons/purchase/__openerp__.py @@ -40,7 +40,7 @@ Dashboard for purchase management that includes: 'author': 'OpenERP SA', 'website': 'http://www.openerp.com', 'images' : ['images/purchase_order.jpeg', 'images/purchase_analysis.jpeg', 'images/request_for_quotation.jpeg'], - 'depends': ['base', 'account', 'stock', 'process', 'procurement'], + 'depends': ['stock', 'process', 'procurement'], 'data': [ 'security/purchase_security.xml', 'security/ir.model.access.csv', diff --git a/addons/sale/__openerp__.py b/addons/sale/__openerp__.py index d626cfe3ff6..eb003dff918 100644 --- a/addons/sale/__openerp__.py +++ b/addons/sale/__openerp__.py @@ -101,9 +101,6 @@ Dashboard for Sales Manager that includes: 'test/sale_report.yml', ], - "js": [ - 'static/src/js/edi_sale_purchase_order.js', - ], 'installable': True, 'active': False, 'certificate': '0058103601429', From bc1ea9c051700be90c41db6030d5d763dfcc723c Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Mon, 17 Oct 2011 15:19:31 +0200 Subject: [PATCH 080/132] [IMP] edi: minor EDI preview template improvements bzr revid: odo@openerp.com-20111017131931-9ak4u7x03bvhgchf --- addons/edi/__openerp__.py | 3 + addons/edi/controllers/edi_view.py | 14 +-- addons/edi/static/src/xml/edi.xml | 151 ++++++++++++++--------------- 3 files changed, 84 insertions(+), 84 deletions(-) diff --git a/addons/edi/__openerp__.py b/addons/edi/__openerp__.py index 60084c4c484..effeea60585 100644 --- a/addons/edi/__openerp__.py +++ b/addons/edi/__openerp__.py @@ -50,6 +50,9 @@ technical OpenERP documentation at http://doc.openerp.com 'static/src/js/edi_invoice.js', 'static/src/js/edi_sale_purchase_order.js', ], + "css": [ + "static/src/css/edi.css" + ], 'installable': True, 'active': False, 'certificate': '002046536359186', diff --git a/addons/edi/controllers/edi_view.py b/addons/edi/controllers/edi_view.py index 324bebed36a..b851053f676 100644 --- a/addons/edi/controllers/edi_view.py +++ b/addons/edi/controllers/edi_view.py @@ -23,7 +23,7 @@ edi_view_template = textwrap.dedent(""" } } var edi_engine = new c.web.EdiView("oe"); - edi_engine.view_edi('%(token)s', '%(db)s'); + edi_engine.view_edi('%(token)s', '%(db)s'); }); @@ -33,8 +33,8 @@ edi_view_template = textwrap.dedent(""" """) def edi_addons(): - #FIXME: hardcoded to be able to test - return 'web,edi,sale,purchase' + #FIXME: hardcoded to be able to test + return 'web,edi' _addons = ['web', 'edi', 'sale', 'purchase'] for addon in openerpweb.addons_module: @@ -47,7 +47,7 @@ def edi_addons(): return _addons class EDIView(web.WebClient): - # http://path.to.web.client:8080/web/view_edi?db=XXX&token=XXXXXXXXXXX + # http://path.to.web.client:8080/web/view_edi?db=XXX&token=XXXXXXXXXXX _cp_path = "/web/view_edi" @openerpweb.httprequest @@ -63,7 +63,7 @@ class EDIView(web.WebClient): content,timestamp = web.concat_files(req.config.addons_path[0], files) # TODO request set the Date of last modif and Etag return req.make_response(content, [('Content-Type', 'application/javascript')]) - + @openerpweb.httprequest def index(self, req, token, db): # script tags @@ -80,7 +80,7 @@ class EDIView(web.WebClient): css = "\n ".join([''%i for i in csslist]) js_files = [str(js_file.split('/')[-1].split('.')[0]) for js_file in self.manifest_glob(req, addons, 'js')] - + r = edi_view_template % { 'javascript': js, 'css': css, @@ -89,6 +89,6 @@ class EDIView(web.WebClient): 'edi_js': js_files } return r - + diff --git a/addons/edi/static/src/xml/edi.xml b/addons/edi/static/src/xml/edi.xml index 9da758f6900..d04502c52f4 100644 --- a/addons/edi/static/src/xml/edi.xml +++ b/addons/edi/static/src/xml/edi.xml @@ -1,62 +1,57 @@