feidee_utils 0.0.4.3 → 0.0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -4
- data/lib/feidee_utils/account.rb +35 -15
- data/lib/feidee_utils/account_group.rb +6 -2
- data/lib/feidee_utils/category.rb +17 -6
- data/lib/feidee_utils/database.rb +33 -17
- data/lib/feidee_utils/kbf.rb +7 -4
- data/lib/feidee_utils/record/accessors.rb +3 -1
- data/lib/feidee_utils/record/namespaced.rb +2 -1
- data/lib/feidee_utils/record/persistent.rb +6 -2
- data/lib/feidee_utils/record.rb +15 -12
- data/lib/feidee_utils/transaction.rb +38 -17
- data/lib/feidee_utils/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9cc0908a9717145ac9f863abad4f5d4ff910a898
|
4
|
+
data.tar.gz: 64d25e89e933a547ba71e6e412b64178dc7a9310
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0fc21e8b91f1f7f44404163478533ffcdffc4c42455bdd0f81881758c1a5faa12e49193d500869d22cf52a416fc2ce4a5a38ad71d5f6552194f265fdbb4649dc
|
7
|
+
data.tar.gz: 3e78c29a78d14c2ca7e0ce2c5cab4c914ae02b2a39a13feb337726e859ba6de0a56cb42bab28c18c602f42c7b0bc828ce6d11c77b7a142e1135622caf23054fe
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@ Feidee Utils
|
|
2
2
|
============
|
3
3
|
[![Build Status](https://travis-ci.org/muyiliqing/feidee_utils.svg?branch=master)](https://travis-ci.org/muyiliqing/feidee_utils)
|
4
4
|
|
5
|
-
Free
|
5
|
+
Free user data from [Feidee](http://www.feidee.com) private backups (.kbf).
|
6
6
|
|
7
7
|
Feidee And The KBF Format
|
8
8
|
-----------
|
@@ -26,9 +26,9 @@ A set of ActiveRecord-like classes are provided to access the information in the
|
|
26
26
|
require 'feidee_utils'
|
27
27
|
|
28
28
|
kbf = FeideeUtils::Kbf.open_file(path_to_kbf_file)
|
29
|
-
database = kbf.
|
30
|
-
all_accounts = database.
|
31
|
-
all_transactions = database.
|
29
|
+
database = kbf.db
|
30
|
+
all_accounts = database.ledger::Account.all
|
31
|
+
all_transactions = database.ledger::Transaction.all
|
32
32
|
```
|
33
33
|
|
34
34
|
For more examples see ```examples/``` (To be added).
|
data/lib/feidee_utils/account.rb
CHANGED
@@ -5,17 +5,34 @@ require 'bigdecimal'
|
|
5
5
|
module FeideeUtils
|
6
6
|
class Account < Record
|
7
7
|
def validate_integrity
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
unless not field["type"] or field["type"] == 0
|
9
|
+
raise "Account type should always be 0, but it's #{field["type"]}.\n" +
|
10
|
+
inspect
|
11
|
+
end
|
12
|
+
unless field["usedCount"] == 0
|
13
|
+
raise "Account usedCount should always be 0," +
|
14
|
+
" but it's #{field["usedCount"]}.\n"+
|
15
|
+
inspect
|
16
|
+
end
|
17
|
+
unless field["uuid"].to_s.empty?
|
18
|
+
raise "Account uuid should always be empty,"+
|
19
|
+
" but it's #{field["uuid"]}.\n" +
|
20
|
+
inspect
|
21
|
+
end
|
22
|
+
unless flat_parent_hierachy?
|
23
|
+
raise "Account hierachy contains more than 2 levels.\n" + inspect
|
24
|
+
end
|
25
|
+
unless (raw_hidden == 1 or raw_hidden == 0)
|
26
|
+
raise "Account hidden should be either 0 or 1," +
|
27
|
+
" but it's #{raw_hidden}.\n" +
|
28
|
+
inspect
|
29
|
+
end
|
13
30
|
end
|
14
31
|
|
15
32
|
def self.validate_global_integrity
|
16
33
|
if self.find_by_id(-1) != nil
|
17
|
-
raise "-1 is used as the parent POID placeholder of a parent account.
|
18
|
-
"Account of POID -1 should not exist."
|
34
|
+
raise "-1 is used as the parent POID placeholder of a parent account." +
|
35
|
+
" Account of POID -1 should not exist."
|
19
36
|
end
|
20
37
|
end
|
21
38
|
|
@@ -36,15 +53,12 @@ module FeideeUtils
|
|
36
53
|
}.freeze
|
37
54
|
|
38
55
|
IgnoredFields = [
|
39
|
-
"tradingEntityPOID",
|
40
|
-
#
|
41
|
-
# earlier versions.
|
42
|
-
"type", # Always 0
|
56
|
+
"tradingEntityPOID", # Foreign key to t_user or maybe t_tradingEntity.
|
57
|
+
"type", # Always 0, removed since database version 73.
|
43
58
|
"usedCount", # Always 0
|
44
59
|
"uuid", # Always empty.
|
45
|
-
#
|
46
|
-
"
|
47
|
-
"clientID", # WTF
|
60
|
+
"code", # Always 0, removed since database version 73.
|
61
|
+
"clientID", # Always equal to poid.
|
48
62
|
].freeze
|
49
63
|
|
50
64
|
define_accessors(FieldMappings)
|
@@ -96,7 +110,9 @@ module FeideeUtils
|
|
96
110
|
|
97
111
|
def children
|
98
112
|
arr = []
|
99
|
-
self.class.database.query(
|
113
|
+
self.class.database.query(
|
114
|
+
"SELECT * FROM #{self.class.table_name} WHERE parent = ?", poid
|
115
|
+
) do |result|
|
100
116
|
result.each do |raw_row|
|
101
117
|
arr << self.class.new(result.columns, result.types, raw_row)
|
102
118
|
end
|
@@ -104,6 +120,10 @@ module FeideeUtils
|
|
104
120
|
arr
|
105
121
|
end
|
106
122
|
|
123
|
+
def to_s
|
124
|
+
"#{name} (Account/#{poid})"
|
125
|
+
end
|
126
|
+
|
107
127
|
class ModifiedAccount < Record::ModifiedRecord
|
108
128
|
define_custom_methods([
|
109
129
|
:balance,
|
@@ -26,9 +26,9 @@ module FeideeUtils
|
|
26
26
|
}.freeze
|
27
27
|
|
28
28
|
IgnoredFields = [
|
29
|
-
"userTradingEntityPOID", #
|
29
|
+
"userTradingEntityPOID", # Foreign key to t_user.
|
30
30
|
"_tempIconName", # Icon name in the app
|
31
|
-
"clientID", #
|
31
|
+
"clientID", # Always equal to poid.
|
32
32
|
].freeze
|
33
33
|
|
34
34
|
define_accessors(FieldMappings)
|
@@ -39,6 +39,10 @@ module FeideeUtils
|
|
39
39
|
2 => :claim,
|
40
40
|
})
|
41
41
|
|
42
|
+
def to_s
|
43
|
+
"#{name} (AccountGroup/#{poid})"
|
44
|
+
end
|
45
|
+
|
42
46
|
# Schema
|
43
47
|
# accountGroupPOID long not null
|
44
48
|
# name varchar(100) not null
|
@@ -10,13 +10,18 @@ module FeideeUtils
|
|
10
10
|
def validate_integrity
|
11
11
|
validate_depth_integrity
|
12
12
|
validate_one_level_path_integrity
|
13
|
-
|
13
|
+
unless field["usedCount"] == 0
|
14
|
+
raise "Category usedCount should always be 0, " +
|
15
|
+
"but it's #{field["usedCount"]}.\n" +
|
16
|
+
inspect
|
17
|
+
end
|
14
18
|
end
|
15
19
|
|
16
20
|
def self.validate_global_integrity
|
17
21
|
project_root_code = 2
|
18
22
|
if TypeEnum[project_root_code] != :project_root
|
19
|
-
raise "The type code of project root has been changed,
|
23
|
+
raise "The type code of project root has been changed," +
|
24
|
+
" please update the code."
|
20
25
|
end
|
21
26
|
|
22
27
|
rows = self.database.execute <<-SQL
|
@@ -26,11 +31,13 @@ module FeideeUtils
|
|
26
31
|
|
27
32
|
if rows.length > 1
|
28
33
|
poids = rows.map do |row| row[0] end
|
29
|
-
raise "More than one category have type project_root.
|
34
|
+
raise "More than one category have type project_root." +
|
35
|
+
" IDs are #{poids.inspect}."
|
30
36
|
elsif rows.length == 1
|
31
37
|
category_name = rows[0][1]
|
32
38
|
if category_name != "projectRoot" and category_name != "root"
|
33
|
-
raise "Category #{category_name} has type project_root.
|
39
|
+
raise "Category #{category_name} has type project_root." +
|
40
|
+
" ID: #{rows[0][0]}."
|
34
41
|
end
|
35
42
|
end
|
36
43
|
end
|
@@ -45,10 +52,10 @@ module FeideeUtils
|
|
45
52
|
}.freeze
|
46
53
|
|
47
54
|
IgnoredFields = [
|
48
|
-
"userTradingEntityPOID", #
|
55
|
+
"userTradingEntityPOID", # Foreign key to t_user.
|
49
56
|
"_tempIconName", # Icon name in the app
|
50
57
|
"usedCount", # Always 0.
|
51
|
-
"clientID", #
|
58
|
+
"clientID", # Always equal to poid.
|
52
59
|
].freeze
|
53
60
|
|
54
61
|
define_accessors(FieldMappings)
|
@@ -59,6 +66,10 @@ module FeideeUtils
|
|
59
66
|
2 => :project_root, # unkown
|
60
67
|
})
|
61
68
|
|
69
|
+
def to_s
|
70
|
+
"#{name} (Category/#{poid})"
|
71
|
+
end
|
72
|
+
|
62
73
|
# Schema
|
63
74
|
# categoryPOID LONG NOT NULL
|
64
75
|
# name varchar(100) NOT NULL
|
@@ -22,12 +22,14 @@ module FeideeUtils
|
|
22
22
|
t_fund_trans
|
23
23
|
t_module_stock_holding
|
24
24
|
t_module_stock_tran
|
25
|
+
t_tradingEntity
|
26
|
+
t_user
|
25
27
|
).freeze
|
26
28
|
|
27
29
|
attr_reader :sqlite_file
|
28
|
-
attr_reader :platform, :
|
30
|
+
attr_reader :platform, :ledger_name, :last_modified_at
|
29
31
|
attr_reader :missing_tables
|
30
|
-
attr_reader :
|
32
|
+
attr_reader :ledger
|
31
33
|
|
32
34
|
def initialize(private_sqlite, strip = false)
|
33
35
|
@sqlite_file = Database.feidee_to_sqlite(private_sqlite)
|
@@ -37,7 +39,8 @@ module FeideeUtils
|
|
37
39
|
extract_metadata
|
38
40
|
drop_unused_tables if strip
|
39
41
|
|
40
|
-
|
42
|
+
# TODO: make Ledger a first class object.
|
43
|
+
@ledger = Record.generate_namespaced_record_classes(self)
|
41
44
|
end
|
42
45
|
|
43
46
|
def sqlite_backup(dest_file_path)
|
@@ -51,8 +54,8 @@ module FeideeUtils
|
|
51
54
|
end
|
52
55
|
|
53
56
|
def validate_global_integrity
|
54
|
-
@
|
55
|
-
@
|
57
|
+
@ledger.constants.each do |const|
|
58
|
+
@ledger.const_get(const).validate_global_integrity if const != :Database
|
56
59
|
end
|
57
60
|
end
|
58
61
|
|
@@ -71,7 +74,10 @@ module FeideeUtils
|
|
71
74
|
|
72
75
|
def drop_unused_tables
|
73
76
|
useful_tables = Tables.values + PotentialUsefulTables
|
74
|
-
useful_tables
|
77
|
+
useful_tables += useful_tables.map do |x|
|
78
|
+
self.class.trash_table_name(x)
|
79
|
+
end
|
80
|
+
useful_tables.sort!
|
75
81
|
|
76
82
|
# TODO: Record all tables droped.
|
77
83
|
(all_tables - useful_tables).each do |table|
|
@@ -85,14 +91,20 @@ module FeideeUtils
|
|
85
91
|
end
|
86
92
|
|
87
93
|
def extract_metadata
|
88
|
-
@platform = self.execute(
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
94
|
+
@platform = self.execute(
|
95
|
+
"SELECT platform from #{Tables[:metadata]}"
|
96
|
+
)[0][0];
|
97
|
+
|
98
|
+
@ledger_name = self.get_first_row(
|
99
|
+
"SELECT accountBookName FROM #{Tables[:profile]};"
|
100
|
+
)[0];
|
101
|
+
|
102
|
+
# This is not recorded in the database, so the lastest lastUpdateTime of
|
103
|
+
# all transactions is chosen.
|
104
|
+
timestamp = self.get_first_row(
|
105
|
+
"SELECT max(lastUpdateTime) FROM #{Tables[:transactions]};"
|
106
|
+
)[0]
|
107
|
+
@last_modified_at = Time.at((timestamp or 0.0) / 1000)
|
96
108
|
end
|
97
109
|
|
98
110
|
class << self
|
@@ -103,13 +115,14 @@ module FeideeUtils
|
|
103
115
|
Header = "SQLite format 3\0".force_encoding("binary")
|
104
116
|
FeideeHeader_iOS = "%$^#&!@_@- -!F\xff\0".force_encoding('binary')
|
105
117
|
FeideeHeader_Android = ("\0" * 13 + "F\xff\0").force_encoding("binary")
|
118
|
+
AllHeaders = [FeideeHeader_iOS, FeideeHeader_Android, Header]
|
106
119
|
|
107
120
|
def feidee_to_sqlite(private_sqlite, sqlite_file = nil)
|
108
121
|
# Discard the first a few bytes content.
|
109
122
|
private_header = private_sqlite.read(Header.length)
|
110
123
|
|
111
|
-
unless
|
112
|
-
raise "Unexpected header #{private_header.inspect}
|
124
|
+
unless AllHeaders.include? private_header
|
125
|
+
raise "Unexpected private sqlite header #{private_header.inspect}."
|
113
126
|
end
|
114
127
|
|
115
128
|
# Write the rest to a tempfile.
|
@@ -123,7 +136,10 @@ module FeideeUtils
|
|
123
136
|
end
|
124
137
|
|
125
138
|
class << self
|
126
|
-
NoDeleteSuffixTables = %w(
|
139
|
+
NoDeleteSuffixTables = %w(
|
140
|
+
account category tag tradingEntity
|
141
|
+
transaction transaction_template
|
142
|
+
)
|
127
143
|
|
128
144
|
def trash_table_name name
|
129
145
|
NoDeleteSuffixTables.each do |core_name|
|
data/lib/feidee_utils/kbf.rb
CHANGED
@@ -9,22 +9,25 @@ module FeideeUtils
|
|
9
9
|
class Kbf
|
10
10
|
DatabaseName = 'mymoney.sqlite'
|
11
11
|
|
12
|
-
attr_reader :zipfile, :
|
12
|
+
attr_reader :zipfile, :db
|
13
13
|
|
14
14
|
def initialize(input_stream)
|
15
|
-
|
15
|
+
Zip::File.open_buffer(input_stream) do |zipfile|
|
16
16
|
zipfile.each do |entry|
|
17
17
|
if entry.name == DatabaseName
|
18
18
|
# Each call to get_input_stream will create a new stream
|
19
19
|
@original_sqlite_db_entry = entry
|
20
|
-
@
|
20
|
+
@db = FeideeUtils::Database.new(entry.get_input_stream, true)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
26
|
def extract_original_sqlite(dest_file_path = nil)
|
27
|
-
FeideeUtils::Database.feidee_to_sqlite(
|
27
|
+
FeideeUtils::Database.feidee_to_sqlite(
|
28
|
+
@original_sqlite_db_entry.get_input_stream,
|
29
|
+
dest_file_path
|
30
|
+
)
|
28
31
|
end
|
29
32
|
|
30
33
|
class << self
|
@@ -12,7 +12,9 @@ module FeideeUtils
|
|
12
12
|
module ClassMethods
|
13
13
|
def define_accessors field_mappings
|
14
14
|
field_mappings.each do |name, key|
|
15
|
-
|
15
|
+
if method_defined? name
|
16
|
+
raise "Accessor #{name} already exists in #{self.name}."
|
17
|
+
end
|
16
18
|
define_method name do field[key] end
|
17
19
|
end
|
18
20
|
end
|
@@ -12,7 +12,8 @@ module FeideeUtils
|
|
12
12
|
@child_classes.add(child_class)
|
13
13
|
end
|
14
14
|
|
15
|
-
# To use Record with different databases, generate a set of classes for
|
15
|
+
# To use Record with different databases, generate a set of classes for
|
16
|
+
# each db
|
16
17
|
def generate_namespaced_record_classes(db)
|
17
18
|
@child_classes ||= Set.new
|
18
19
|
this = self
|
@@ -36,13 +36,17 @@ module FeideeUtils
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def find_by_id(id)
|
39
|
-
raw_result = database.query(
|
39
|
+
raw_result = database.query(
|
40
|
+
"SELECT * FROM #{self.table_name} WHERE #{self.id_field_name} = ?",
|
41
|
+
id
|
42
|
+
)
|
40
43
|
|
41
44
|
raw_row = raw_result.next
|
42
45
|
return nil if raw_row == nil
|
43
46
|
|
44
47
|
if raw_result.next != nil
|
45
|
-
raise "Getting more than one result with the same ID #{id}
|
48
|
+
raise "Getting more than one result with the same ID #{id} " +
|
49
|
+
"in table #{self.table_name}."
|
46
50
|
end
|
47
51
|
|
48
52
|
self.new(raw_result.columns, raw_result.types, raw_row)
|
data/lib/feidee_utils/record.rb
CHANGED
@@ -6,18 +6,21 @@ require 'feidee_utils/record/modified_record'
|
|
6
6
|
|
7
7
|
module FeideeUtils
|
8
8
|
# The implementation here is wired.
|
9
|
-
# The goal is to create a class hierachy similar to ActiveRecord, where every
|
10
|
-
# subclass of ActiveRecord::Base. Class methods,
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
9
|
+
# The goal is to create a class hierachy similar to ActiveRecord, where every
|
10
|
+
# table is represented by a subclass of ActiveRecord::Base. Class methods,
|
11
|
+
# attribute accessors and almost all other functionalities are provided by
|
12
|
+
# ActiveRecord::Base. For example, Base.all(), Base.find_by_id() are tied to
|
13
|
+
# a specific table in a specific database.
|
14
|
+
# The problem we are solving here is not the same as ActiveRecord. In
|
15
|
+
# ActiveRecord, the databases are static, i.e. they won't be changed at
|
16
|
+
# runtime. Meanwhile, in our case, new databases can be created at runtime,
|
17
|
+
# when a new KBF backup file is uploaded. Furthermore, multiple instances of
|
18
|
+
# different databases can co-exist at the same time. To provide the same
|
19
|
+
# syntax as ActiveRecord, a standalone "Base" class has to be created for each
|
20
|
+
# database.
|
21
|
+
# In our implementation, when a new database is created, a subclass of Record
|
22
|
+
# is created in a new namepsace. For each subclass of Record, a new subclass
|
23
|
+
# is copied to the new namespace, with it's database method overloaded.
|
21
24
|
class Record
|
22
25
|
attr_reader :field, :field_type
|
23
26
|
|
@@ -25,30 +25,37 @@ module FeideeUtils
|
|
25
25
|
unless buyer_account_poid != 0 and seller_account_poid != 0
|
26
26
|
raise TransferLackBuyerOrSellerException,
|
27
27
|
"Both buyer and seller should be set in a transfer. " +
|
28
|
-
"Buyer account POID: #{buyer_account_poid}.
|
28
|
+
"Buyer account POID: #{buyer_account_poid}. " +
|
29
|
+
"Seller account POID: #{seller_account_poid}.\n" +
|
29
30
|
inspect
|
30
31
|
end
|
31
32
|
unless buyer_category_poid == 0 and seller_category_poid == 0
|
32
33
|
raise TransferWithCategoryException,
|
33
34
|
"Neither buyer or seller category should be set in a transfer. " +
|
34
|
-
"Buyer category POID: #{buyer_category_poid}.
|
35
|
+
"Buyer category POID: #{buyer_category_poid}. " +
|
36
|
+
"Seller category POID: #{seller_category_poid}.\n" +
|
35
37
|
inspect
|
36
38
|
end
|
37
39
|
else
|
38
40
|
unless (buyer_account_poid == 0) ^ (seller_account_poid == 0)
|
39
41
|
raise InconsistentBuyerAndSellerException,
|
40
|
-
"Exactly one of buyer and seller should be set in a non-transfer
|
41
|
-
"
|
42
|
+
"Exactly one of buyer and seller should be set in a non-transfer " +
|
43
|
+
"transaction. " +
|
44
|
+
"Buyer account POID: #{buyer_account_poid}. " +
|
45
|
+
"Seller account POID: #{seller_account_poid}.\n" +
|
42
46
|
inspect
|
43
47
|
end
|
44
48
|
|
45
|
-
# We could enforce that category is set to the matching party (buyer or
|
46
|
-
# However the implementation could handle all
|
47
|
-
# Thus no extra check is
|
49
|
+
# We could enforce that category is set to the matching party (buyer or
|
50
|
+
# seller) of account. However the implementation could handle all
|
51
|
+
# situations, as long as only one of them is set. Thus no extra check is
|
52
|
+
# done here.
|
48
53
|
unless buyer_category_poid == 0 or seller_category_poid == 0
|
49
54
|
raise InconsistentCategoryException,
|
50
|
-
"Only one of buyer and seller category should be set in a
|
51
|
-
"
|
55
|
+
"Only one of buyer and seller category should be set in a " +
|
56
|
+
"non-transfer transaction. " +
|
57
|
+
"Buyer category POID: #{buyer_category_poid}. " +
|
58
|
+
"Seller category POID: #{seller_category_poid}.\n" +
|
52
59
|
inspect
|
53
60
|
end
|
54
61
|
end
|
@@ -56,7 +63,8 @@ module FeideeUtils
|
|
56
63
|
unless raw_buyer_deduction == raw_seller_addition
|
57
64
|
raise InconsistentAmountException,
|
58
65
|
"Buyer and seller should have the same amount set. " +
|
59
|
-
"Buyer deduction: #{buyer_deduction}
|
66
|
+
"Buyer deduction: #{buyer_deduction}. "+
|
67
|
+
"Seller_addition: #{seller_addition}.\n" +
|
60
68
|
inspect
|
61
69
|
end
|
62
70
|
end
|
@@ -78,8 +86,10 @@ module FeideeUtils
|
|
78
86
|
valid = true
|
79
87
|
valid &&= transfers[0] != nil
|
80
88
|
valid &&= transfers[1] != nil
|
81
|
-
valid &&=
|
82
|
-
|
89
|
+
valid &&=
|
90
|
+
transfers[0].buyer_account_poid == transfers[1].buyer_account_poid
|
91
|
+
valid &&=
|
92
|
+
transfers[0].seller_account_poid == transfers[1].seller_account_poid
|
83
93
|
raise TransfersNotPaired.new([uuid] + transfers) unless valid
|
84
94
|
end
|
85
95
|
end
|
@@ -102,13 +112,13 @@ module FeideeUtils
|
|
102
112
|
define_entity_accessor :seller_account_poid, :account
|
103
113
|
|
104
114
|
IgnoredFields = [
|
105
|
-
"creatorTradingEntityPOID",
|
106
|
-
"modifierTradingEntityPOID",
|
107
|
-
"ffrom", # The signature of the App
|
115
|
+
"creatorTradingEntityPOID", # Foreign key to to_user.
|
116
|
+
"modifierTradingEntityPOID", # Foreign key to to_user.
|
117
|
+
"ffrom", # The signature of the App.
|
108
118
|
"photoName", # To be added
|
109
119
|
"photoNeedUpload", # To be added
|
110
|
-
"relationUnitPOID", #
|
111
|
-
"clientID", #
|
120
|
+
"relationUnitPOID", # Foreign key to t_tradingEntity: Merchant.
|
121
|
+
"clientID", # Always 0 for ealier versions, or equal to poid.
|
112
122
|
"FSourceKey", # WTF
|
113
123
|
].freeze
|
114
124
|
|
@@ -190,6 +200,17 @@ module FeideeUtils
|
|
190
200
|
end
|
191
201
|
end
|
192
202
|
|
203
|
+
def to_s
|
204
|
+
if is_transfer?
|
205
|
+
"Transfer #{amount.to_f} from #{buyer_account} to #{seller_account}"
|
206
|
+
elsif is_initial_balance?
|
207
|
+
"Balance of #{revised_account} set to #{revised_amount.to_f}"
|
208
|
+
else
|
209
|
+
"Transaction of #{revised_amount.to_f} on #{revised_account} in " +
|
210
|
+
"#{category}"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
193
214
|
class ModifiedTransaction < ModifiedRecord
|
194
215
|
define_custom_methods([
|
195
216
|
:created_at,
|
data/lib/feidee_utils/version.rb
CHANGED