# Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com) # # MIT License (MIT) # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # from __future__ import unicode_literals """ Merges (syncs) incoming doclist into the database Called when: importing .txt files importing bulk records from .csv files For regular types, deletes the record and recreates it for special types: `DocType`, `Module Def`, `DocType Mapper` there are subclasses To use:: set_doc(doclist, ovr=1, ingore=1, noupdate=1) """ import webnotes from webnotes.model.doc import Document # this variable is a flag that transfer process is on, to the on_update # method so that if there are other processes on import, it can do so in_transfer = 0 no_sync = { "Report": ["disabled"] } def set_doc(doclist, ovr=0, ignore=1, onupdate=1): """ Wrapper function to sync a record """ if doclist[0].doctype == "DocType": from webnotes.model.sync import merge_doctype return merge_doctype(doclist, force=ovr) global in_transfer dt = doclist[0]['doctype'] if webnotes.conn.exists(doclist[0]['doctype'], doclist[0]['name']): # exists, merge if possible if dt == 'DocType Mapper': ud = UpdateDocTypeMapper(doclist) else: ud = UpdateDocument(doclist) else: ud = UpdateDocument(doclist) in_transfer = 1 ud.sync() in_transfer = 0 return '\n'.join(ud.log) class UpdateDocument: def __init__(self, in_doclist=[]): self.in_doclist = in_doclist self.doc = Document(fielddata = in_doclist[0]) self.old_doc = None self.modified = self.doc.modified # make a copy self.doclist = [] self.log = [] self.exists = 0 # sync def sync(self): is_mod = self.is_modified() if (not self.exists) or (is_mod): webnotes.conn.begin() if self.exists: self.delete_existing() self.save() self.run_on_update() self.update_modified() webnotes.conn.commit() # check modified def is_modified(self): try: timestamp = webnotes.conn.get_value(self.doc.doctype, self.doc.name, "modified") except Exception ,e: if(e.args[0]==1146): return else: raise e if timestamp: self.exists = 1 if str(timestamp) == self.doc.modified: self.log.append('%s %s, No change' % (self.doc.doctype, self.doc.name)) else: return 1 # delete existing def delete_existing(self): from webnotes.model import delete_doc self.old_doc = webnotes.doc(self.doc.doctype, self.doc.name) delete_doc(self.doc.doctype, self.doc.name, force=1) # update modified timestamp def update_modified(self): webnotes.conn.sql("""update `tab{doctype}` SET modified=%s WHERE name=%s""".format(doctype=self.doc.doctype), (self.modified, self.doc.name)) def save(self): # parent self.update_no_sync(self.doc) self.doc.save(new = 1, check_links=0) self.doclist = [self.doc] self.save_children() def save_children(self): for df in self.in_doclist[1:]: self.save_one_doc(df) def update_no_sync(self, d): if d.doctype in no_sync and self.old_doc: for fieldname in no_sync[d.doctype]: d.fields[fieldname] = self.old_doc.fields.get(fieldname) def save_one_doc(self, df, as_new=1): d = Document(fielddata = df) d.save(new = as_new, check_links=0) self.doclist.append(d) def run_on_update(self): from webnotes.model.code import get_obj so = get_obj(doc=self.doc, doclist=self.doclist) if hasattr(so, 'on_update'): so.on_update() class UpdateDocumentMerge(UpdateDocument): def __init__(self, in_doclist): self.to_update_doctype = [] UpdateDocument.__init__(self, in_doclist) def delete_existing(self): pass def get_id(self, d): pass def to_update(self, d): return 1 def child_exists(self, d): return self.get_id(d) def on_save(self): pass def save(self): if self.exists: # save main doc self.keep_values(self.doc) self.doc.save(check_links=0) self.doclist.append(self.doc) self.save_children() self.on_save() self.log.append('Updated %s' % self.doc.name) else: UpdateDocument.save(self) def save_children(self): for df in self.in_doclist[1:]: d = Document(fielddata = df) # update doctype? if d.doctype in self.to_update_doctype: # update this record? if self.to_update(d): # is it new? if self.child_exists(d): self.keep_values(d) d.save(check_links=0) self.log.append('updated %s, %s' % (d.doctype, d.name)) else: d.save(1, check_links=0) self.log.append('new %s' % d.doctype) self.doclist.append(d) def keep_values(self, d): if hasattr(self, 'get_orignal_values'): ov = self.get_orignal_values(d) if ov: d.fields.update(ov) class UpdateDocTypeMapper(UpdateDocumentMerge): """ Merge `DocType Mapper` """ def __init__(self, in_doclist): UpdateDocumentMerge.__init__(self, in_doclist) self.to_update_doctype = ['Field Mapper Detail', 'Table Mapper Detail'] def get_id(self, d): if d.doctype=='Field Mapper Detail': return webnotes.conn.sql("select name from `tabField Mapper Detail` where from_field=%s and to_field=%s and match_id=%s and parent=%s", (d.from_field, d.to_field, d.match_id, d.parent)) elif d.doctype=='Table Mapper Detail': return webnotes.conn.sql("select name from `tabTable Mapper Detail` where from_table=%s and to_table = %s and match_id=%s and validation_logic=%s and parent=%s", (d.from_table, d.to_table, d.match_id, d.validation_logic, d.parent)) def get_orignal_values(self, d): if d.doctype in ['Field Mapper Detail', 'Table Mapper Detail']: return {'name': self.get_id(d)[0][0]}