Moved to new history api.

This commit is contained in:
Martin Edenhofer 2013-09-29 18:40:42 +02:00
parent 4e2288e9a4
commit 6b21e155e9
20 changed files with 316 additions and 353 deletions

View file

@ -9,6 +9,7 @@ class App.TicketHistory extends App.ControllerModal
@fetch(@ticket_id)
fetch: (@ticket_id) ->
# get data
@ajax(
id: 'ticket_history',
@ -19,18 +20,43 @@ class App.TicketHistory extends App.ControllerModal
# load collections
App.Event.trigger 'loadAssets', data.assets
# load history collections
App.History.deleteAll()
App.Collection.load( type: 'History', data: data.history )
# render page
@render()
@render(data.history)
)
render: ->
render: ( items, orderClass = '' ) ->
for item in items
if item.object is 'Ticket'
ticket = App.Ticket.find( item.o_id )
item.link = '#ticket/zoom/' + ticket.id
item.title = ticket.title
item.object = 'Ticket'
else if item.object is 'Ticket::Article'
article = App.TicketArticle.find( item.o_id )
ticket = App.Ticket.find( article.ticket_id )
item.link = '#ticket/zoom/' + ticket.id + '/' + article.id
item.title = article.subject || ticket.title
item.object = 'Article'
else if item.object is 'User'
user = App.User.find( item.o_id )
item.link = '#user/zoom/' + item.o_id
item.title = user.displayName()
item.object = 'User'
item.created_by = App.User.find( item.created_by_id )
# set cache
@historyListCache = items
@html App.view('agent_ticket_history')(
objects: App.History.search()
items: items
orderClass: orderClass
@historyListCache
)
@modalShow()
@ -43,25 +69,9 @@ class App.TicketHistory extends App.ControllerModal
sortorder: (e) ->
e.preventDefault()
isSorted = @el.find('.sorted')
idDown = @el.find('[data-type="sortorder"]').hasClass('down')
if isSorted.length
@sortstate = 'notsorted'
@html App.view('agent_ticket_history')(
objects: App.History.search()
state: @sortstate
)
if idDown
@render( @historyListCache, 'up' )
else
@sortstate = 'sorted'
@html App.view('agent_ticket_history')(
objects: App.History.search().reverse()
state: @sortstate
)
@modalShow()
# enable user popups
@userPopups()
# show frontend times
@delay( @frontendTimeUpdate, 200, 'ui-time-update' )
@render( @historyListCache.reverse(), 'down' )

View file

@ -1,20 +0,0 @@
class App.History extends App.Model
@configure 'History', 'name'
@extend Spine.Model.Ajax
@url: @apiPath + '/histories'
@_fillUp: (data) ->
# add user
data.created_by = App.User.find( data.created_by_id )
# add possible actions
if data.history_attribute_id
data.attribute = App.HistoryAttribute.find( data.history_attribute_id )
if data.history_type_id
data.type = App.HistoryType.find( data.history_type_id )
if data.history_object_id
data.object = App.HistoryObject.find( data.history_object_id )
return data

View file

@ -1,4 +0,0 @@
class App.HistoryAttribute extends App.Model
@configure 'HistoryAttribute', 'name'
@extend Spine.Model.Ajax
@url: @apiPath + '/history_attributes'

View file

@ -1,4 +0,0 @@
class App.HistoryObject extends App.Model
@configure 'HistoryObject', 'name'
@extend Spine.Model.Ajax
@url: @apiPath + '/history_objects'

View file

@ -1,4 +0,0 @@
class App.HistoryType extends App.Model
@configure 'HistoryType', 'name'
@extend Spine.Model.Ajax
@url: @apiPath + '/history_types'

View file

@ -3,44 +3,40 @@
<div class="modal-header">
<a href="#" class="close">&times;</a>
<h2 class="modal-title"><%- @T( 'History' ) %></h2>
<a href="#" data-type="sortorder" id="sortorder" class="<%= @state %>"><%- @T( 'Change order' ) %></a>
<a href="#" data-type="sortorder" class="<%= @orderClass %>"><%- @T( 'Change order' ) %></a>
</div>
<div class="modal-body">
<% open = false %>
<% for object in @objects: %>
<% object['history_object_display'] = object['history_object'] %>
<% if object['history_object'] is 'Ticket::Article': %>
<% object['history_object_display'] = 'Article' %>
<% end %>
<% if lasttime isnt object['created_at'] || last_user isnt object['created_by_id']: %>
<% for item in @items: %>
<% if lasttime isnt item['created_at'] || last_user isnt item['created_by_id']: %>
<% if open: %>
</ul>
<hr>
<% end %>
<% open = true %>
<% last_user = object['created_by_id'] %>
<% lasttime = object['created_at'] %>
<span class="user-popover" data-id="<%= object.created_by.id %>"><%= object.created_by.displayName() %></span> -
<span class="humanTimeFromNow" data-time="<%- object.created_at %>">?</span>
<% last_user = item['created_by_id'] %>
<% lasttime = item['created_at'] %>
<span class="user-popover" data-id="<%= item.created_by.id %>"><%= item.created_by.displayName() %></span> -
<span class="humanTimeFromNow" data-time="<%- item.created_at %>">?</span>
<ul>
<% end %>
<li>
<% if ( object['history_type'] is 'notification' || object['history_type'] is 'email' ): %>
<%= object['history_type'] %>
<% if object['value_from']: %>
"<%= object['value_from'] %>" <%- @T( 'sent to' ) %>
<% if ( item['type'] is 'notification' || item['type'] is 'email' ): %>
<%= item['type'] %>
<% if item['value_from']: %>
"<%= item['value_from'] %>" <%- @T( 'sent to' ) %>
<% end %>
<% if object['value_to']: %>
"<%= object['value_to'] %>"
<% if item['value_to']: %>
"<%= item['value_to'] %>"
<% end %>
<% else: %>
<%= object['history_type'] %> <%= object['history_object_display'] %> <% if object['history_attribute']: %>"<%= object['history_attribute'] %>"<% end %>
<% if object['value_from']: %>
<%- @T( 'from' ) %> "<%= object['value_from'] %>"
<%= item['type'] %> <%= item['object'] %> <% if item['attribute']: %>"<%= item['attribute'] %>"<% end %>
<% if item['value_from']: %>
<%- @T( 'from' ) %> "<%= item['value_from'] %>"
<% end %>
<% if object['value_to']: %>
<%- @T( 'to' ) %> "<%= object['value_to'] %>"
<% if item['value_to']: %>
<%- @T( 'to' ) %> "<%= item['value_to'] %>"
<% end %>
<% end %>
</li>

View file

@ -130,53 +130,11 @@ class TicketsController < ApplicationController
return if !ticket_permission( ticket )
# get history of ticket
history = ticket.history_get
history = ticket.history_get(true)
# get related assets
assets = ticket.assets({})
history_list = []
history.each do |item|
assets = item.assets(assets)
item_tmp = item.attributes
if item['history_object'] == 'Ticket::Article'
item_temp['type'] = 'Article ' + item['type'].to_s
else
item_tmp['type'] = 'Ticket ' + item['type'].to_s
end
item_tmp['history_type'] = item.history_type.name
item_tmp['history_object'] = item.history_object.name
if item.history_attribute
item_tmp['history_attribute'] = item.history_attribute.name
end
item_tmp.delete( 'history_attribute_id' )
item_tmp.delete( 'history_object_id' )
item_tmp.delete( 'history_type_id' )
item_tmp.delete( 'o_id' )
item_tmp.delete( 'updated_at' )
if item_tmp['id_to'] == nil && item_tmp['id_from'] == nil
item_tmp.delete( 'id_to' )
item_tmp.delete( 'id_from' )
end
if item_tmp['value_to'] == nil && item_tmp['value_from'] == nil
item_tmp.delete( 'value_to' )
item_tmp.delete( 'value_from' )
end
if item_tmp['related_history_object_id'] == nil
item_tmp.delete( 'related_history_object_id' )
end
if item_tmp['related_o_id'] == nil
item_tmp.delete( 'related_o_id' )
end
history_list.push item_tmp
end
# return result
render :json => {
:assets => assets,
:history => history_list,
}
render :json => history
end
# GET /api/v1/ticket_merge_list/1

View file

@ -19,8 +19,14 @@ class ApplicationModel < ActiveRecord::Base
after_update :activity_stream_update
after_destroy :activity_stream_destroy
after_create :history_create
after_update :history_update
after_destroy :history_destroy
# create instance accessor
class << self; attr_accessor :activity_stream_support_config end
class << self
attr_accessor :activity_stream_support_config, :history_support_config
end
@@import_class_list = ['Ticket', 'Ticket::Article', 'History', 'Ticket::State', 'Ticket::Priority', 'Group', 'User' ]
@ -418,7 +424,7 @@ class OwnModel < ApplicationModel
=begin
serve methode to configure activity stream support for this model
serve methode to configure and enable activity stream support for this model
class Model < ApplicationModel
activity_stream_support :role => 'Admin'
@ -432,7 +438,7 @@ end
=begin
log object create activity stream
log object create activity stream, if configured - will be executed automatically
model = Model.find(123)
model.activity_stream_create
@ -445,7 +451,7 @@ log object create activity stream
=begin
log object update activity stream
log object update activity stream, if configured - will be executed automatically
model = Model.find(123)
model.activity_stream_update
@ -453,12 +459,13 @@ log object update activity stream
=end
def activity_stream_update
return if !self.changed?
activity_stream_log( 'updated', self['updated_by_id'] )
end
=begin
delete object activity stream
delete object activity stream, will be executed automatically
model = Model.find(123)
model.activity_stream_destroy
@ -471,15 +478,141 @@ delete object activity stream
=begin
serve methode to configure and enable history support for this model
class Model < ApplicationModel
history_support
end
class Model < ApplicationModel
history_support :ignore_attributes => { :article_count => true }
end
=end
def self.history_support(data = {})
@history_support_config = data
end
=begin
log object create history, if configured - will be executed automatically
model = Model.find(123)
model.history_create
=end
def history_create
return if !self.class.history_support_config
# puts self.changes.inspect
self.history_log( 'created', self.created_by_id )
end
=begin
log object update history with all updated attributes, if configured - will be executed automatically
model = Model.find(123)
model.history_update
=end
def history_update
return if !self.class.history_support_config
return if !self.changed?
# return if it's no update
return if self.new_record?
# new record also triggers update, so ignore new records
changes = self.changes
return if changes['id'] && !changes['id'][0]
#puts 'updated' + self.changes.inspect
# TODO: Swop it to config file later
ignore_attributes = {
:created_at => true,
:updated_at => true,
:created_by_id => true,
:updated_by_id => true,
:article_count => true,
:create_article_type_id => true,
:create_article_sender_id => true,
}
changes.each {|key, value|
# do not log created_at and updated_at attributes
next if ignore_attributes[key.to_sym] == true
# get attribute name
attribute_name = key.to_s
if attribute_name[-3,3] == '_id'
attribute_name = attribute_name[ 0, attribute_name.length-3 ]
end
value_id = []
if key.to_s[-3,3] == '_id'
value_id[0] = value[0]
value_id[1] = value[1]
if self.respond_to?( attribute_name )
relation_class = self.send(attribute_name).class
if relation_class
relation_model = relation_class.lookup( :id => value_id[0] )
if relation_model
if relation_model['name']
value[0] = relation_model['name']
elsif relation_model.respond_to?('fullname')
value[0] = relation_model.send('fullname')
end
end
relation_model = relation_class.lookup( :id => value_id[1] )
if relation_model
if relation_model['name']
value[1] = relation_model['name']
elsif relation_model.respond_to?('fullname')
value[1] = relation_model.send('fullname')
end
end
end
end
end
data = {
:history_attribute => attribute_name,
:value_from => value[0],
:value_to => value[1],
:id_from => value_id[0],
:id_to => value_id[1],
}
#puts "HIST NEW " + data.inspect
self.history_log( 'updated', self.updated_by_id, data )
}
end
=begin
delete object history, will be executed automatically
model = Model.find(123)
model.history_destroy
=end
def history_destroy
History.remove( self.class.to_s, self.id )
end
=begin
destory object dependencies, will be executed automatically
=end
def destroy_dependencies
# delete history
History.remove( self.class.to_s, self.id )
end
end

View file

@ -7,7 +7,7 @@ module ApplicationModel::HistoryLogBase
create history entry for this object
organization = Organization.find(123)
result = organization.history_create( 'created', user_id )
result = organization.history_log( 'created', user_id )
returns
@ -15,7 +15,7 @@ returns
=end
def history_create (type, user_id, data = {})
def history_log (type, user_id, data = {})
data[:o_id] = self['id']
data[:history_type] = type
data[:history_object] = self.class.name
@ -57,8 +57,25 @@ returns
=end
def history_get
History.list( self.class.name, self['id'] )
def history_get(fulldata = false)
list = History.list( self.class.name, self['id'] )
return list if !fulldata
# get related objects
assets = {}
list.each {|item|
record = Kernel.const_get( item['object'] ).find( item['o_id'] )
assets = record.assets(assets)
if item['related_object']
record = Kernel.const_get( item['related_object'] ).find( item['related_o_id'] )
assets = record.assets(assets)
end
}
return {
:history => list,
:assets => assets,
}
end
end

View file

@ -5,5 +5,7 @@ class Group < ApplicationModel
belongs_to :email_address
belongs_to :signature
validates :name, :presence => true
activity_stream_support :role => 'Admin'
history_support
end

View file

@ -36,6 +36,9 @@ add a new history entry for an object
def self.add(data)
# return if we run import mode
return if Setting.get('import_mode')
# lookups
if data[:history_type]
history_type = self.type_lookup( data[:history_type] )
@ -50,7 +53,7 @@ add a new history entry for an object
end
history_attribute_id = nil
if data[:history_attribute]
history_attribute = self.history_attribute_lookup( data[:history_attribute] )
history_attribute = self.attribute_lookup( data[:history_attribute] )
history_attribute_id = history_attribute.id
end
@ -130,8 +133,40 @@ return all history entries of an object
).
order('created_at ASC, id ASC')
end
list = []
history.each do |item|
data = item.attributes
data['object'] = self.object_lookup_id( data['history_object_id'] ).name
data['type'] = self.type_lookup_id( data['history_type_id'] ).name
data.delete('history_object_id')
data.delete('history_type_id')
return history
if data['history_attribute_id']
data['attribute'] = self.attribute_lookup_id( data['history_attribute_id'] ).name
end
data.delete('history_attribute_id')
data.delete( 'updated_at' )
if data['id_to'] == nil && data['id_from'] == nil
data.delete( 'id_to' )
data.delete( 'id_from' )
end
if data['value_to'] == nil && data['value_from'] == nil
data.delete( 'value_to' )
data.delete( 'value_from' )
end
if data['related_history_object_id'] != nil
data['related_object'] = self.object_lookup_id( data['related_history_object_id'] ).name
end
data.delete( 'related_history_object_id' )
if data['related_o_id'] == nil
data.delete( 'related_o_id' )
end
list.push data
end
list
end
private
@ -198,7 +233,18 @@ return all history entries of an object
return history_object
end
def self.history_attribute_lookup( name )
def self.attribute_lookup_id( id )
# use cache
return @@cache_attribute[ id ] if @@cache_attribute[ id ]
# lookup
history_attribute = History::Attribute.find(id)
@@cache_attribute[ id ] = history_attribute
return history_attribute
end
def self.attribute_lookup( name )
# use cache
return @@cache_attribute[ name ] if @@cache_attribute[ name ]

View file

@ -1,159 +0,0 @@
# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/
require 'history'
class Observer::History < ActiveRecord::Observer
observe :ticket, 'ticket::_article', :user, :organization, :group
def after_create(record)
# return if we run import mode
return if Setting.get('import_mode')
puts "HISTORY OBSERVER, object created #{ record.class.name }.find(#{ record.id })"
# puts record.inspect
user_id = record.created_by_id || UserInfo.current_user_id || 1
# log activity stream
if record.respond_to?('history_create')
record.history_create( 'created', user_id )
end
end
def before_update(record)
# return if we run import mode
return if Setting.get('import_mode')
# puts 'before_update'
current = record.class.find(record.id)
# do not send anything if nothing has changed
return if current.attributes == record.attributes
puts "HISTORY OBSERVER, object will be updated #{ record.class.name.to_s}.find(#{ current.id.to_s })"
# puts 'current'
# puts current.inspect
# puts 'record'
# puts record.inspect
diff = differences_from?(current, record)
#puts ' DIFF'
#puts ' ' + diff.inspect
#puts ' CURRENT USER ID ' + UserInfo.current_user_id.to_s
map = {
:group_id => {
:lookup_object => Group,
:lookup_name => 'name',
},
:owner_id => {
:lookup_object => User,
:lookup_method => 'fullname',
},
:ticket_state_id => {
:lookup_object => Ticket::State,
:lookup_name => 'name',
},
:ticket_priority_id => {
:lookup_object => Ticket::Priority,
:lookup_name => 'name',
}
}
# TODO: Swop it to config file later
ignore_attributes = {
:created_at => true,
:updated_at => true,
:created_by_id => true,
:updated_by_id => true,
:article_count => true,
:create_article_type_id => true,
:create_article_sender_id => true,
}
user_id = record.created_by_id || UserInfo.current_user_id || 1
history_logged = false
diff.each do |key, value_ids|
# do not log created_at and updated_at attributes
next if ignore_attributes[key.to_sym] == true
#puts " CHANGED: #{key} is #{value_ids.inspect}"
# check if diff are ids, if yes do lookup
if value_ids[0].to_s == value_ids[1].to_s
#puts 'NEXT!!'
next
end
# check if diff are ids, if yes do lookup
value = []
if map[key.to_sym] && map[key.to_sym][:lookup_object]
value[0] = ''
value[1] = ''
# name base
if map[key.to_sym][:lookup_name]
if map[key.to_sym][:lookup_name].class != Array
map[key.to_sym][:lookup_name] = [ map[key.to_sym][:lookup_name] ]
end
map[key.to_sym][:lookup_name].each do |item|
if value[0] != ''
value[0] = value[0] + ' '
end
value[0] = value[0] + map[key.to_sym][:lookup_object].find(value_ids[0])[item.to_sym].to_s
if value[1] != ''
value[1] = value[1] + ' '
end
value[1] = value[1] + map[key.to_sym][:lookup_object].find(value_ids[1])[item.to_sym].to_s
end
end
# method base
if map[key.to_sym][:lookup_method]
value[0] = map[key.to_sym][:lookup_object].find( value_ids[0] ).send( map[key.to_sym][:lookup_method] )
value[1] = map[key.to_sym][:lookup_object].find( value_ids[1] ).send( map[key.to_sym][:lookup_method] )
end
# if not, fill diff data to value, empty value_ids
else
value = value_ids
value_ids = []
end
# get attribute name
attribute_name = key.to_s
if attribute_name.scan(/^(.*)_id$/).first
attribute_name = attribute_name.scan(/^(.*)_id$/).first.first
end
history_logged = true
data = {
:history_attribute => attribute_name,
:value_from => value[0],
:value_to => value[1],
:id_from => value_ids[0],
:id_to => value_ids[1],
}
# log activity stream
if record.respond_to?('history_create')
record.history_create( 'updated', user_id, data )
end
end
end
def differences_from?(one, other)
h = {}
one.attributes.each_pair do |key, value|
if one[key] != other[key]
h[key.to_sym] = [ one[key], other[key] ]
end
end
h
end
end

View file

@ -6,6 +6,8 @@ class Organization < ApplicationModel
has_and_belongs_to_many :users
validates :name, :presence => true
activity_stream_support :role => 'Admin'
history_support
end

View file

@ -7,7 +7,9 @@ class Ticket < ApplicationModel
after_create :notify_clients_after_create
after_update :notify_clients_after_update
after_destroy :notify_clients_after_destroy
activity_stream_support :role => 'User'
history_support
belongs_to :group
has_many :articles, :class_name => 'Ticket::Article', :after_add => :cache_update, :after_remove => :cache_update
@ -119,9 +121,6 @@ returns
def destroy_dependencies
# delete history
History.remove( self.class.to_s, self.id )
# delete articles
self.articles.destroy_all
end

View file

@ -14,6 +14,7 @@ class Ticket::Article < ApplicationModel
after_destroy :notify_clients_after_destroy
activity_stream_support
history_support
attr_accessor :attachments

View file

@ -15,7 +15,7 @@ returns
=end
def history_create (type, user_id, data = {})
def history_log (type, user_id, data = {})
# if Ticketdata[:data[:Article has changed, remember related ticket to be able
# to show article changes in ticket history
@ -28,40 +28,4 @@ returns
History.add(data)
end
=begin
get log activity for this article
article = Ticket::Article.find(123)
result = article.history_get()
returns
result = [
{
:history_type => 'created',
:history_object => 'Ticket::Article',
:created_by_id => 3,
:created_at => "2013-08-19 20:41:33",
},
{
:history_type => 'updated',
:history_object => 'Ticket::Article',
:history_attribute => 'from',
:o_id => 1,
:id_to => nil,
:id_from => nil,
:value_from => "Some Body",
:value_to => "Some Body Else",
:created_by_id => 3,
:created_at => "2013-08-19 20:41:33",
},
]
=end
def history_get
History.list( self.class.name, self['id'] )
end
end

View file

@ -15,7 +15,7 @@ returns
=end
def history_create (type, user_id, data = {})
def history_log (type, user_id, data = {})
data[:o_id] = self['id']
data[:history_type] = type
@ -58,8 +58,25 @@ returns
=end
def history_get
History.list( self.class.name, self['id'], 'Ticket::Article' )
def history_get(fulldata = false)
list = History.list( self.class.name, self['id'], 'Ticket::Article' )
return list if !fulldata
# get related objects
assets = {}
list.each {|item|
record = Kernel.const_get( item['object'] ).find( item['o_id'] )
assets = record.assets(assets)
if item['related_object']
record = Kernel.const_get( item['related_object'] ).find( item['related_o_id'] )
assets = record.assets(assets)
end
}
return {
:history => list,
:assets => assets,
}
end
end

View file

@ -20,6 +20,7 @@ class User < ApplicationModel
store :preferences
activity_stream_support :role => 'Admin'
history_support
=begin

View file

@ -27,7 +27,6 @@ module Zammad
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
config.active_record.observers =
'observer::_session',
'observer::_history',
'observer::_ticket::_first_response',
'observer::_ticket::_last_contact',
'observer::_ticket::_close_time',

View file

@ -185,6 +185,7 @@ class HistoryTest < ActiveSupport::TestCase
:firstname => 'Bob',
:lastname => 'Smith',
:email => 'somebody@example.com',
:active => true,
:updated_by_id => User.lookup( :login => 'nicole.braun@zammad.org' ).id,
:created_by_id => User.lookup( :login => 'nicole.braun@zammad.org' ).id,
},
@ -194,6 +195,7 @@ class HistoryTest < ActiveSupport::TestCase
:firstname => 'Bob',
:lastname => 'Master',
:email => 'master@example.com', },
:active => false,
},
:history_check => [
{
@ -207,6 +209,13 @@ class HistoryTest < ActiveSupport::TestCase
:value_from => 'Smith',
:value_to => 'Master',
},
{
:history_object => 'User',
:history_type => 'updated',
:history_attribute => 'email',
:value_from => 'somebody@example.com',
:value_to => 'master@example.com',
},
],
},
@ -323,29 +332,29 @@ class HistoryTest < ActiveSupport::TestCase
# puts '--------'
# puts history_item.inspect
# puts history_item.history_object.name
next if history_item.history_object.name != check_item[:history_object]
next if history_item.history_type.name != check_item[:history_type]
next if history_item['object'] != check_item[:history_object]
next if history_item['type'] != check_item[:history_type]
if check_item[:history_attribute]
next if check_item[:history_attribute] != history_item.history_attribute.name
next if check_item[:history_attribute] != history_item['attribute']
end
match = true
if history_item.history_type.name == check_item[:history_type]
assert( true, "History type #{history_item.history_type.name} found!")
if history_item['type'] == check_item[:history_type]
assert( true, "History type #{history_item['type']} found!")
end
if check_item[:history_attribute]
assert_equal( check_item[:history_attribute], history_item.history_attribute.name, "check history attribute #{check_item[:history_attribute]}")
assert_equal( check_item[:history_attribute], history_item['attribute'], "check history attribute #{check_item[:history_attribute]}")
end
if check_item[:value_from]
assert_equal( check_item[:value_from], history_item.value_from, "check history :value_from #{history_item.value_from} ok")
assert_equal( check_item[:value_from], history_item['value_from'], "check history :value_from #{history_item['value_from']} ok")
end
if check_item[:value_to]
assert_equal( check_item[:value_to], history_item.value_to, "check history :value_to #{history_item.value_to} ok")
assert_equal( check_item[:value_to], history_item['value_to'], "check history :value_to #{history_item['value_to']} ok")
end
if check_item[:id_from]
assert_equal( check_item[:id_from], history_item.id_from, "check history :id_from #{history_item.id_from} ok")
assert_equal( check_item[:id_from], history_item['id_from'], "check history :id_from #{history_item['id_from']} ok")
end
if check_item[:id_to]
assert_equal( check_item[:id_to], history_item.id_to, "check history :id_to #{history_item.id_to} ok")
assert_equal( check_item[:id_to], history_item['id_to'], "check history :id_to #{history_item['id_to']} ok")
end
}
assert( match, "history check not matched! #{check_item.inspect}")