feidee_utils 0.0.4.3 → 0.0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
[](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