odoo/addons/edi/wizard/wizard_edi_import.py

489 lines
22 KiB
Python

##############################################################################
#
# Copyright (c) 2005-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
#
# $Id: wizard_edi_import.py 1825 2005-12-13 11:04:20Z ede $
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import ir
import time
import os
from edi_exchange import edi_exchange
import wizard
from osv import osv
import pooler
_import_form = '''<?xml version="1.0"?>
<form string="EDI file import">
<separator string="Import the following files" colspan="4" />
<field name="ediimportdir" colspan="4" />
<field name="current" />
<field name="error" />
</form>'''
_import_fields = { 'ediimportdir' : { 'string' : 'EDI Import Dir',
'type' : 'char',
'size' : 100,
'default' : lambda *a: '/edi/reception',
'required' : True
},
'current' : { 'string' : 'Current',
'type' : 'boolean',
'default' : lambda *a: True,
},
'error' : { 'string' : 'Error',
'type' : 'boolean',
'default' : lambda *a: False,
},
}
_import_done_form = '''<?xml version="1.0"?>
<form string="EDI file imported">
<separator string="EDI file imported" colspan="4" />
</form>'''
_import_done_fields = {}
def _child_of_partner(cr, uid, child, parent):
if child==parent:
return True
direct_parent_list=pooler.get_pool(cr.dbname).get('res.partner').read(cr, uid, [child], ['parent_id'])
#print str(direct_parent_list)
if len(direct_parent_list)!=1:
return False
if not direct_parent_list[0]['parent_id']:
return False
direct_parent=direct_parent_list[0]['parent_id'][0]
if direct_parent and direct_parent!='':
return _child_of_partner( cr, uid, direct_parent, parent)
else:
return False
def _prepare_import(self, cr, uid, data, context):
files=[]
for (type, dir) in [('current', 'encours'), ('error', 'erreur')]:
if (data['form'][type]):
edi_path = os.path.join(data['form']['ediimportdir'], dir)
if not os.path.isdir(edi_path):
os.makedirs(edi_path)
for file in os.listdir(edi_path):
#print file[:9]
if file[:9]=="COMMANDE.":
files.append((file, os.path.join(data['form']['ediimportdir'], dir, file)))
for (filename, file) in files:
#print "Importing %s" % filename
FirstInt=True
FirstOrd=True
sr = {'sender':None, 'receiver':None}
sale_orders = []
log_id=pooler.get_pool(cr.dbname).get('edi.log').create(cr, uid, {})
logline=pooler.get_pool(cr.dbname).get('edi.log.line')
logline.create(cr, uid, {
'log_id': log_id,
'logdesc': "Checking %s" % filename,
})
try:
status= edi_status()
fh = open(file, "r")
for line in fh:
line_type, line_content = edi_exchange.parse_line(line)
if line_type == "HINT":
if not FirstInt:
sale_orders.append(sale_order_o)
FirstOrd=True
sale_order_o = sale_order(cr, uid, sr)
elif line_type == "HPTY":
if not FirstOrd:
FirstInt=False
elif line_type == "DART":
FirstOrd=False
sale_order_o.parse_line(line_type, line_content, status)
sale_orders.append(sale_order_o)
finally:
fh.close()
for (order_id, order_timestamp, sender, message) in status.messages:
logline.create(cr, uid, { 'log_id': log_id,
'logdesc': "\t%s" % message,
'timestamp': order_timestamp,
'order_num': order_id
})
logline.create(cr, uid, { 'log_id': log_id,
'logdesc': "Messages:%s\tErrors:%s\tWarnings:%s" % (len(status.messages), status.error_count, status.warning_count),
})
logline.create(cr, uid, { 'log_id': log_id,
'logdesc': "Finished Checking %s" % filename,
})
if status.error_count==0:
print "Integrating %s" % filename
logline.create(cr, uid, { 'log_id': log_id,
'logdesc': "Importing %s" % filename,
})
for order in sale_orders:
order.store()
logline.create(cr, uid, { 'log_id': log_id,
'logdesc': "Import Finished",
})
if not os.path.isdir(os.path.join(data['form']['ediimportdir'],'archive')):
os.mkdir(os.path.join(data['form']['ediimportdir'],'archive'))
os.rename(file, os.path.join(data['form']['ediimportdir'],'archive', filename))
logline.create(cr, uid, { 'log_id': log_id,
'logdesc': "Moved %s to archive" % filename,
})
else:
ids=pooler.get_pool(cr.dbname).get('res.users').search(cr,1, [('roles_id', 'ilike', 'EDI')])
for id in ids:
pooler.get_pool(cr.dbname).get('res.request').create(cr, uid, { 'act_to': id,
'name' : "Error while importing %s" % filename,
'ref_doc1' : "edi.log,%s" % log_id,
'state' : 'active',
})
try:
os.rename(file, os.path.join(data['form']['ediimportdir'],'erreur', filename))
logline.create(cr, uid, { 'log_id': log_id,
'logdesc': "Moved %s to erreur" % filename,
})
except:
logline.create(cr, uid, { 'log_id': log_id,
'logdesc': "Couldn't move %s to erreur" % filename,
})
cr.commit()
return {}
def _do_import(self, cr, uid, data, context):
pass
class wiz_edi_import(wizard.interface):
states = {
'init' : {
'actions' : [],
'result' : { 'type' : 'form', 'arch' : _import_form, 'fields' : _import_fields, 'state' : [('end', 'Cancel'),('check', 'Check EDI')]},
},
'check' : {
'actions' : [ _prepare_import ],
# 'result' : { 'type' : 'print', 'report' : 'edi.import-results', 'state' : [('do_import', 'Import EDI'), ('end', 'End')] },
'result' : { 'type' : 'form', 'arch' : _import_done_form, 'fields': _import_done_fields, 'state' : [('end','end')]},
},
'do_import' : {
'actions' : [ _do_import ],
'result' : { 'type' : 'form', 'arch' : _import_done_form, 'fields' : _import_done_fields, 'state' : [('end','end')]},
},
}
class sale_order:
def __init__(self, cr, uid, sr):
self.cr=cr
self.uid=uid
self.shop_id=pooler.get_pool(cr.dbname).get('sale.shop').search(cr, uid, [])[0]
self.pricelist_id = None
self.order_lines=[]
self.partner_id=sr['sender']
self.partner_order_id=0
self.partner_invoice_id=0
self.sr = sr
self.timesatmp_edi=time.strftime('%Y%m%d%H%M')
self.ordernum = '0'
self.deliverdate=None
self.note = ''
def store(self):
if not hasattr(self, 'partner_invoice_id'):
self.partner_invoice_id = self.partner_id
if not hasattr(self, 'partner_shipping_id'):
self.partner_shipping_id = self.partner_invoice_id
order_id = pooler.get_pool(cr.dbname).get('sale.order').create(self.cr, self.uid, { 'partner_id': self.partner_id,
'partner_order_id': self.partner_order_id,
'partner_invoice_id': self.partner_invoice_id,
'partner_shipping_id': self.partner_shipping_id,
'shop_id': self.shop_id,
'pricelist_id': self.pricelist_id,
'client_order_ref': self.ordernum,
'date_order': self.orderdate,
'note': self.note,
})
for orderline in self.order_lines:
orderline.store(order_id)
def addline(self, line):
self.orderlines.append(line)
def parse_line(self, line_type, line_content, status):
if hasattr(self, "_parse_%s" % line_type):
myFct=getattr(self, "_parse_%s" % line_type)
myFct(line_content, status)
else:
status.add_warning("ignoring line type: %s" % line_type, self.ordernum, self.timestamp_edi, self.sr['sender'])
def _parse_HINT(self, line_content, status):
self.timestamp_edi="%s%s" % (line_content["date"], line_content["time"])
self.sr['sender'] = line_content['sender']
partners=pooler.get_pool(cr.dbname).get('res.partner').search(self.cr, self.uid, [('ean13','=',line_content["receiver"]),])
if len(partners) != 1:
status.add_error("unknown receiver: %s" % line_content["receiver"], self.ordernum, self.timestamp_edi, self.sr['sender'])
else:
self.sr['receiver']=partners[0]
thisadd=pooler.get_pool(cr.dbname).get('res.users').read(self.cr, self.uid, [self.uid], ['address_id'])[0]['address_id'][0]
partner=pooler.get_pool(cr.dbname).get('res.partner.address').read(self.cr, self.uid, [thisadd], ['partner_id'])[0]['partner_id'][0]
if not partner or partner!=self.sr['receiver']:
status.add_error("This message is not for us (%s)" % line_content["receiver"], self.ordernum, self.timestamp_edi, self.sr['sender'])
def _parse_HGEN(self, line_content, status):
if line_content["order-num"]=='':
status.add_error("No client order reference", self.ordernum, self.timestamp_edi, self.sr['sender'])
self.ordernum=line_content["order-num"]
self.orderdate=line_content["order-date"]
if line_content["message-type"]!="ORDERS93A" and line_content["message-type"]!="ORDERS96A" and line_content["message-type"]!="GENCOD02303" and line_content["message-type"]!="GENCOD08604":
status.add_error("Unknown message type %s" % line_content["message-type"], self.ordernum, self.timestamp_edi, self.sr['sender'])
def _parse_HDEL(self, line_content, status):
if (not self.deliverdate or self.deliverdate > line_content['deliv-date']) and line_content['deliv-q'] in ('137', '200'):
self.deliverdate = line_content['deliv-date']
if self.deliverdate < self.orderdate:
status.add_error("%s (order date) is after %s (delivery date)" % (self.orderdate,self.deliverdate), self.ordernum, self.timestamp_edi, self.sr['sender'])
def _parse_HPTY(self, line_content, status):
partner_table = pooler.get_pool(cr.dbname).get('res.partner')
partners = partner_table.search(self.cr, self.uid, [('ean13', '=', line_content['partner-code'])])
if partners and len(partners) == 1:
default_addresses = pooler.get_pool(cr.dbname).get('res.partner.address').search(self.cr, self.uid, [('partner_id', '=', partners[0]), ('type', 'ilike', 'default')])
if not default_addresses:
default_addresses = pooler.get_pool(cr.dbname).get('res.partner.address').search(self.cr, self.uid, [('partner_id', '=', partners[0]), ('type', 'ilike', '')])
if not default_addresses:
default_addresses = pooler.get_pool(cr.dbname).get('res.partner.address').search(self.cr, self.uid, [('partner_id', '=', partners[0])])
self.hpty_dispatchers[line_content['partner-type']](self, partners[0], default_addresses, line_content, status)
else:
status.add_error("unknown %s: %s" % (line_content["partner-type"], line_content["partner-code"]), self.ordernum, self.timestamp_edi, self.sr['sender'])
def _parse_HPTYBY(self, partner, default_addresses, line_content, status):
partner_table = pooler.get_pool(cr.dbname).get('res.partner')
self.sr['sender'] = partner
self.partner_id = partner
self.partner_order_id = default_addresses[0]
self.pricelist_id = ir.ir_get(self.cr, self.uid, 'meta', 'product.pricelist', [('res.partner', self.partner_id)])[0][2]
orders=pooler.get_pool(cr.dbname).get("sale.order").search(self.cr, self.uid, [('client_order_ref', 'ilike', self.ordernum), ('partner_order_id',"=",self.partner_order_id)])
if orders and len(orders)>0:
status.add_warning("This client order reference (%s) already exists for this client" % self.ordernum, self.ordernum, self.timestamp_edi, self.sr['sender'])
def _parse_HPTYSU(self, partner, default_addresses, line_content, status):
partner_table = pooler.get_pool(cr.dbname).get('res.partner')
if not (partner_table._is_related_to(self.cr, self.uid, [partner], self.sr['receiver'])[0] or self.sr['receiver'] == partner):
status.add_error("unknown %s: %s" % (line_content["partner-type"], line_content["partner-code"]), self.ordernum, self.timestamp_edi, self.sr['sender'])
def _parse_HPTYDP(self, partner, default_addresses, line_content, status):
shipping_addresses=pooler.get_pool(cr.dbname).get('res.partner.address').search(self.cr, self.uid, [('partner_id','=',partner), ('type', 'ilike', 'delivery')])
if len(shipping_addresses) < 1:
self.partner_shipping_id=default_addresses[0]
else:
self.partner_shipping_id=shipping_addresses[0]
invoice_addresses=pooler.get_pool(cr.dbname).get('res.partner.address').search(self.cr, self.uid, [('partner_id','=',partner), ('type', 'ilike', 'invoice')])
if len(invoice_addresses) < 1:
self.partner_invoice_id=default_addresses[0]
else:
self.partner_invoice_id=invoice_addresses[0]
def _parse_HPTYIV(self, partner, default_addresses, line_content, status):
invoice_addresses=pooler.get_pool(cr.dbname).get('res.partner.address').search(self.cr, self.uid, [('partner_id','=',partner), ('type', 'ilike', 'invoice')])
if len(invoice_addresses) < 1:
self.partner_invoice_id=default_addresses[0]
else:
self.partner_invoice_id=invoice_addresses[0]
hpty_dispatchers = { 'BY' : _parse_HPTYBY, 'SU' : _parse_HPTYSU, 'DP' : _parse_HPTYDP, 'IV' : _parse_HPTYIV }
def _parse_HFTX(self, line_content, status):
self.note+=line_content['text']+'\n'
def _parse_DART(self, line_content, status):
products=pooler.get_pool(cr.dbname).get('product.product').search(self.cr, self.uid, [('ean13','=',line_content["barcode"]),])
#sale_order_line_o=sale_order_line(self.cr, self.uid, self.deliverdate, self.partner_invoice_id, status)
sale_order_line_o=sale_order_line(self.cr, self.uid, self, self.deliverdate)
if len(products) != 1:
status.add_error("unknown product: %s" % line_content["barcode"], self.ordernum, self.timestamp_edi, self.sr['sender'])
return
else:
sale_order_line_o.product=products[0]
sale_order_line_o.product_ean=line_content["barcode"]
if (line_content["unit21"]==''):
status.add_warning("Using default Unit Of Measure", self.ordernum, self.timestamp_edi, self.sr['sender'])
else:
uoms=pooler.get_pool(cr.dbname).get('product.uom').search(self.cr, self.uid, [('name', 'ilike', line_content["unit21"]),])
if len(uoms) != 1:
status.add_error("unknown uom: %s" % line_content["unit21"], self.ordernum, self.timestamp_edi, self.sr['sender'])
return
else:
sale_order_line_o.uom=uoms[0]
sale_order_line_o.quantity=float(line_content["quantity21"])
sale_order_line_o.uoc_quantity=float(line_content["quantity59"])
sale_order_line_o.lineid=line_content["line-num"]
sale_order_line_o.partner_address=None
sale_order_line_o.price=line_content["price"]
if sale_order_line_o.partner==0:
partner=self.partner_id
else:
partner=sale_order_line_o.partner
pricelist_id = ir.ir_get(self.cr, self.uid, 'meta', 'product.pricelist', [('res.partner', partner)])[0][2]
sale_order_line_o.price = pooler.get_pool(cr.dbname).get('product.pricelist').price_get(self.cr, self.uid, [pricelist_id], sale_order_line_o.product, sale_order_line_o.quantity)[pricelist_id]
sale_order_line_o.pricelist_id=pricelist_id
if float(line_content["price"])!=sale_order_line_o.price:
status.add_warning("Price from EDI (%s) different from what we have (%s) for product %s" % (str(float(line_content["price"])), sale_order_line_o.price, line_content["barcode"]), self.ordernum, self.timestamp_edi, self.sr['sender'])
product_infos = pooler.get_pool(cr.dbname).get('product.product').read(self.cr, self.uid, [sale_order_line_o.product])[0]
if line_content['price-unit']=="":
status.add_warning("Blank Unit Of Price for product %s should be %s" % (line_content['barcode'], product_infos['uos_id'][1]), self.ordernum, self.timestamp_edi, self.sr['sender'])
sale_order_line_o.price_unit= product_infos['uos_id'][0]
elif product_infos['uos_id'][1] != line_content['price-unit']:
status.add_error('Invalid Unit Of Price for product %s Should be "%s" instead of "%s"' % (line_content['barcode'], product_infos['uos_id'][1], line_content["price-unit"]), self.ordernum, self.timestamp_edi, self.sr['sender'])
else:
sale_order_line_o.price_unit= product_infos['uos_id'][0]
sale_order_line_o.price_unit_customer=float(line_content['hint-price'])
sale_order_line_o.check(status)
self.order_lines.append(sale_order_line_o)
def _parse_DDEL(self, line_content, status):
sale_order_line_o = self.order_lines[len(self.order_lines)-1]
sale_order_line_o.deliv_date = "%s%s" % (line_content["deliv-date"], line_content["deliv-time"])
def _parse_DPTY(self, line_content, status):
if len(self.order_lines)<1:
status.add_error("no DART line parsed before this DPTY line", self.ordernum, self.timestamp_edi, self.sr['sender'])
else:
sale_order_line_o = self.order_lines[len(self.order_lines)-1]
partners=pooler.get_pool(cr.dbname).get('res.partner').search(self.cr, self.uid, [('ean13','=',line_content["shop-barcode"]),])
if len(partners) != 1:
status.add_error("unknown address: %s" % line_content["shop-barcode"], self.ordernum, self.timestamp_edi, self.sr['sender'])
elif not _child_of_partner(self.cr, self.uid, partners[0], self.sr['sender']):
status.add_error("unknown address: %s" % line_content["shop-barcode"], self.ordernum, self.timestamp_edi, self.sr['sender'])
else:
sale_order_line_o.partner_address=partners[0]
def _parse_DPID(self, line_content, status):
if len(self.order_lines)<1:
status.add_error("no DART line parsed before this DPTY line", self.ordernum, self.timestamp_edi, self.sr['sender'])
else:
sale_order_line_o = self.order_lines[len(self.order_lines)-1]
sale_order_line_o.note+=line_content['ident-art']+'\n'
def _parse_DFTX(self, line_content, status):
if len(self.order_lines)<1:
status.add_error("no DART line parsed before this DPTY line", self.ordernum, self.timestamp_edi, self.sr['sender'])
else:
sale_order_line_o = self.order_lines[len(self.order_lines)-1]
sale_order_line_o.note+=line_content['text']+'\n'
class sale_order_line:
def __init__(self, cr, uid, sale_order_o, deliv_date):
#, partner_address, status):
self.cr=cr
self.uid=uid
self.insertdict={}
self.partner_address=None
if deliv_date:
self.deliv_date=deliv_date
self.note=''
self.product=0
self.sale_order= sale_order_o
self.price_unit=0
if sale_order_o.partner_invoice_id!=0:
self.partner=pooler.get_pool(cr.dbname).get('res.partner.address').read(self.cr, self.uid, [sale_order_o.partner_invoice_id], ['partner_id'])[0]['partner_id'][0]
else:
self.partner=0
#self.status=status
def check(self, status):
self.cr.execute('select id from product_packaging where product_id=%d and qty=%f limit 1', (self.product, float(self.uoc_quantity)))
packs = self.cr.fetchone()
if packs is None or not len(packs):
status.add_error('Invalid package for product %s (%s)' % (self.product_ean, float(self.uoc_quantity)), self.sale_order.ordernum, self.sale_order.timestamp_edi, self.sale_order.sr['sender'])
else:
self.pack_id=packs[0]
#print "PriceList: %s, product: %s, quantity: %s " % (self.pricelist_id, self.product, self.quantity)
try:
dico=pooler.get_pool(cr.dbname).get('sale.order.line').product_id_change(self.cr, self.uid, [], self.pricelist_id, self.product, int(float(self.quantity)))
self.insertdict.update({'product_uos_qty': dico['value']['product_uos_qty'], 'product_uos': dico['value']['product_uos'][0], 'price_unit': dico['value']['price_unit']})
except:
status.add_error('No price defined for product %s, line ommited !' % (self.product_ean,), self.sale_order.ordernum, self.sale_order.timestamp_edi, self.sale_order.sr['sender'])
#print str(dico)
# Checking the unit used for the price computation
# product_infos = pooler.get_pool(cr.dbname).get('product.product').read(self.cr, self.uid, [self.product])[0]
# if product_infos['uos_id'][1] != self.price_unit:
# status.add_warning('Invalid unit for Sale price : Should be "%s"' % product_infos['uos_id'][1], self.sale_order.ordernum, self.sale_order.timestamp_edi, self.sale_order.sr['sender'])
# Checking the price
# if unit_price != self.price:
# status.add_warning('Invalid price', self.sale_order.ordernum, self.sale_order.timestamp_edi, self.sale_order.sr['sender'])
def store (self, order_id):
insertdict.update( { 'order_id': order_id,
'product_id': self.product,
'product_uom_qty': self.quantity,
'name': "%s%s" %(order_id, self.lineid),
'address_allotment_id': self.partner_address,
'notes': self.note,
'product_packaging': self.pack_id,
# 'unit_price': self.price_unit,
'price_unit': self.price,
'price_unit_customer': self.price_unit_customer,
})
self.unit_price=self.price
if hasattr(self, 'uom'):
insertdict['product_uom'] = self.uom
else:
insertdict['product_uom'], desc = pooler.get_pool(cr.dbname).get('product.product').read(self.cr, self.uid, [self.product], ['uom_id'])[0]['uom_id']
if hasattr(self, 'deliv_date'):
insertdict['date_planned'] = self.deliv_date
id=pooler.get_pool(cr.dbname).get('sale.order.line').create(self.cr, self.uid, insertdict)
return id
class edi_status:
def __init__(self):
self.error_count=0
self.warning_count=0
self.messages=[]
def add_error(self, message, order_id, timestamp_edi, sender):
self.messages.append((order_id, timestamp_edi, sender, "ERROR:\t%s" % message))
self.error_count+=1
def add_warning(self, message, order_id, timestamp_edi, sender):
self.messages.append((order_id, timestamp_edi, sender, "WARNING:\t%s" % message))
self.warning_count+=1
wiz_edi_import('edi.import')