ledger_sync 1.0.10 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +36 -20
- data/README.md +105 -66
- data/ledger_sync.gemspec +1 -0
- data/lib/ledger_sync.rb +23 -4
- data/lib/ledger_sync/adaptors/operation.rb +28 -28
- data/lib/ledger_sync/adaptors/quickbooks_online/account/mapping.rb +325 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/account/operations/create.rb +54 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/account/operations/find.rb +38 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/account/operations/update.rb +61 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/account/searcher.rb +67 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/adaptor.rb +18 -16
- data/lib/ledger_sync/adaptors/quickbooks_online/bill/operations/create.rb +65 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/bill/operations/find.rb +37 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/bill/operations/update.rb +69 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/bill/searcher.rb +28 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/customer/operations/create.rb +6 -6
- data/lib/ledger_sync/adaptors/quickbooks_online/customer/operations/find.rb +1 -3
- data/lib/ledger_sync/adaptors/quickbooks_online/customer/operations/update.rb +6 -9
- data/lib/ledger_sync/adaptors/quickbooks_online/customer/searcher.rb +2 -38
- data/lib/ledger_sync/adaptors/quickbooks_online/deposit/operations/create.rb +61 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/deposit/operations/find.rb +36 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/deposit/operations/update.rb +65 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/deposit/searcher.rb +28 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/expense/mapping.rb +15 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/expense/operations/create.rb +68 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/expense/operations/find.rb +39 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/expense/operations/update.rb +72 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/expense/searcher.rb +28 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/journal_entry/mapping.rb +14 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/journal_entry/operations/create.rb +56 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/journal_entry/operations/find.rb +34 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/journal_entry/operations/update.rb +60 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/journal_entry/searcher.rb +28 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/operation.rb +30 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/payment/operations/create.rb +8 -22
- data/lib/ledger_sync/adaptors/quickbooks_online/payment/operations/find.rb +0 -2
- data/lib/ledger_sync/adaptors/quickbooks_online/payment/operations/update.rb +6 -22
- data/lib/ledger_sync/adaptors/quickbooks_online/searcher.rb +45 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/transfer/operations/create.rb +52 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/transfer/operations/find.rb +36 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/transfer/operations/update.rb +57 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/vendor/operations/create.rb +46 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/vendor/operations/find.rb +36 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/vendor/operations/update.rb +51 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/vendor/searcher.rb +64 -0
- data/lib/ledger_sync/adaptors/searcher.rb +3 -1
- data/lib/ledger_sync/adaptors/test/account/operations/create.rb +53 -0
- data/lib/ledger_sync/adaptors/test/account/operations/find.rb +38 -0
- data/lib/ledger_sync/adaptors/test/account/operations/invalid.rb +25 -0
- data/lib/ledger_sync/adaptors/test/account/operations/update.rb +50 -0
- data/lib/ledger_sync/adaptors/test/account/operations/valid.rb +31 -0
- data/lib/ledger_sync/adaptors/test/account/searcher.rb +40 -0
- data/lib/ledger_sync/adaptors/test/adaptor.rb +4 -4
- data/lib/ledger_sync/adaptors/test/customer/operations/create.rb +4 -6
- data/lib/ledger_sync/adaptors/test/customer/operations/find.rb +0 -2
- data/lib/ledger_sync/adaptors/test/customer/operations/update.rb +4 -6
- data/lib/ledger_sync/adaptors/test/customer/searcher.rb +2 -2
- data/lib/ledger_sync/adaptors/test/expense/operations/create.rb +54 -0
- data/lib/ledger_sync/adaptors/test/expense/operations/find.rb +39 -0
- data/lib/ledger_sync/adaptors/test/expense/operations/update.rb +57 -0
- data/lib/ledger_sync/adaptors/test/payment/operations/create.rb +4 -20
- data/lib/ledger_sync/adaptors/test/payment/operations/find.rb +0 -2
- data/lib/ledger_sync/adaptors/test/payment/operations/update.rb +4 -20
- data/lib/ledger_sync/adaptors/test/transfer/operations/create.rb +45 -0
- data/lib/ledger_sync/adaptors/test/transfer/operations/find.rb +36 -0
- data/lib/ledger_sync/adaptors/test/transfer/operations/update.rb +48 -0
- data/lib/ledger_sync/adaptors/test/vendor/operations/create.rb +47 -0
- data/lib/ledger_sync/adaptors/test/vendor/operations/find.rb +34 -0
- data/lib/ledger_sync/adaptors/test/vendor/operations/invalid.rb +21 -0
- data/lib/ledger_sync/adaptors/test/vendor/operations/update.rb +44 -0
- data/lib/ledger_sync/adaptors/test/vendor/operations/valid.rb +27 -0
- data/lib/ledger_sync/adaptors/test/vendor/searcher.rb +41 -0
- data/lib/ledger_sync/error/resource_errors.rb +24 -0
- data/lib/ledger_sync/resource.rb +24 -64
- data/lib/ledger_sync/resource_attribute.rb +50 -0
- data/lib/ledger_sync/resource_attribute/dirty_mixin.rb +49 -0
- data/lib/ledger_sync/resource_attribute/mixin.rb +90 -0
- data/lib/ledger_sync/resource_attribute/reference.rb +9 -0
- data/lib/ledger_sync/resource_attribute/reference/many.rb +34 -0
- data/lib/ledger_sync/resource_attribute/reference/one.rb +33 -0
- data/lib/ledger_sync/resource_attribute_set.rb +58 -0
- data/lib/ledger_sync/resources/account.rb +14 -0
- data/lib/ledger_sync/resources/bill.rb +17 -0
- data/lib/ledger_sync/resources/bill_line_item.rb +11 -0
- data/lib/ledger_sync/resources/customer.rb +5 -4
- data/lib/ledger_sync/resources/deposit.rb +16 -0
- data/lib/ledger_sync/resources/deposit_line_item.rb +11 -0
- data/lib/ledger_sync/resources/expense.rb +19 -0
- data/lib/ledger_sync/resources/expense_line_item.rb +11 -0
- data/lib/ledger_sync/resources/journal_entry.rb +13 -0
- data/lib/ledger_sync/resources/journal_entry_line_item.rb +12 -0
- data/lib/ledger_sync/resources/payment.rb +5 -3
- data/lib/ledger_sync/resources/transfer.rb +15 -0
- data/lib/ledger_sync/resources/vendor.rb +12 -0
- data/lib/ledger_sync/result.rb +0 -24
- data/lib/ledger_sync/type/boolean.rb +17 -0
- data/lib/ledger_sync/type/date.rb +17 -0
- data/lib/ledger_sync/type/float.rb +17 -0
- data/lib/ledger_sync/type/integer.rb +17 -0
- data/lib/ledger_sync/type/reference_many.rb +27 -0
- data/lib/ledger_sync/type/reference_one.rb +26 -0
- data/lib/ledger_sync/type/string.rb +17 -0
- data/lib/ledger_sync/type/value.rb +12 -0
- data/lib/ledger_sync/type/value_mixin.rb +19 -0
- data/lib/ledger_sync/util/hash_helpers.rb +16 -1
- data/lib/ledger_sync/util/resources_builder.rb +36 -9
- data/lib/ledger_sync/version.rb +1 -1
- metadata +92 -7
- data/lib/ledger_sync/adaptors/quickbooks_online/customer/operations/upsert.rb +0 -42
- data/lib/ledger_sync/adaptors/quickbooks_online/payment/operations/upsert.rb +0 -53
- data/lib/ledger_sync/adaptors/test/customer/operations/upsert.rb +0 -42
- data/lib/ledger_sync/adaptors/test/payment/operations/upsert.rb +0 -53
- data/lib/ledger_sync/sync.rb +0 -108
- data/lib/ledger_sync/util/coordinator.rb +0 -72
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ledger_sync/type/value'
|
4
|
+
|
5
|
+
Gem.find_files('ledger_sync/type/**/*.rb').each do |path|
|
6
|
+
require path
|
7
|
+
end
|
8
|
+
|
9
|
+
Gem.find_files('ledger_sync/resource_attribute/**/*.rb').each do |path|
|
10
|
+
require path
|
11
|
+
end
|
12
|
+
|
13
|
+
module LedgerSync
|
14
|
+
class ResourceAttribute
|
15
|
+
include Fingerprintable::Mixin
|
16
|
+
include SimplySerializable::Mixin
|
17
|
+
|
18
|
+
attr_accessor :value
|
19
|
+
attr_reader :name,
|
20
|
+
:reference,
|
21
|
+
:type
|
22
|
+
|
23
|
+
def initialize(name:, type:, value: nil)
|
24
|
+
@name = name.to_sym
|
25
|
+
|
26
|
+
type = type.new if type.respond_to?(:new) && !type.is_a?(Type::Value)
|
27
|
+
|
28
|
+
raise "Invalid Type: #{type}" unless type.is_a?(ActiveModel::Type::Value)
|
29
|
+
|
30
|
+
@type = type
|
31
|
+
|
32
|
+
@value = value
|
33
|
+
end
|
34
|
+
|
35
|
+
def cast(value)
|
36
|
+
type.cast(value)
|
37
|
+
end
|
38
|
+
|
39
|
+
# This is for ActiveModel::Dirty, since we define @attributes
|
40
|
+
# def forgetting_assignment; end
|
41
|
+
|
42
|
+
def reference?
|
43
|
+
is_a?(Reference)
|
44
|
+
end
|
45
|
+
|
46
|
+
def valid_with?(value:)
|
47
|
+
type.valid_without_casting?(value: value)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Helper methods for adding dirty tracked attributes
|
4
|
+
module LedgerSync
|
5
|
+
class ResourceAttribute
|
6
|
+
module DirtyMixin
|
7
|
+
def self.included(base)
|
8
|
+
base.include(ActiveModel::Dirty)
|
9
|
+
base.extend(ClassMethods)
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def dirty_attribute(*names)
|
14
|
+
names.each do |name|
|
15
|
+
class_eval do
|
16
|
+
attr_reader name
|
17
|
+
define_attribute_methods name # From ActiveModel
|
18
|
+
dirty_attributes[name] = {
|
19
|
+
name: name
|
20
|
+
}
|
21
|
+
|
22
|
+
define_method("#{name}=") do |val|
|
23
|
+
send("#{name}_will_change!") unless val == instance_variable_get("@#{name}")
|
24
|
+
instance_variable_set("@#{name}", val)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def dirty_attributes
|
31
|
+
@dirty_attributes ||= {}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Normally you would just call `changes_applied`, but because we
|
36
|
+
# define an `@attributes` instance variable, the `ActiveModel::Dirty`
|
37
|
+
# mixin assumes it is a list of attributes in their format (spoiler: it
|
38
|
+
# isn't). So this is copying the code from `changes_applied` and setting
|
39
|
+
# `@mutations_from_database` to the value it would receive if
|
40
|
+
# `@attributes` did not exist.
|
41
|
+
def save
|
42
|
+
# changes_applied # Code reproduced below.
|
43
|
+
@mutations_before_last_save = mutations_from_database
|
44
|
+
# forget_attribute_assignments # skipped as our attributes do not implement this method
|
45
|
+
@mutations_from_database = ActiveModel::ForcedMutationTracker.new(self) # Manually set to expected value
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../resource_attribute_set'
|
4
|
+
require_relative 'dirty_mixin'
|
5
|
+
|
6
|
+
# Mixin for attribute functionality
|
7
|
+
module LedgerSync
|
8
|
+
class ResourceAttribute
|
9
|
+
module Mixin
|
10
|
+
def self.included(base)
|
11
|
+
base.include(DirtyMixin)
|
12
|
+
base.extend(ClassMethods)
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def _define_attribute_methods(name)
|
17
|
+
class_eval do
|
18
|
+
define_attribute_methods name
|
19
|
+
|
20
|
+
define_method name do
|
21
|
+
resource_attributes[name].value
|
22
|
+
end
|
23
|
+
|
24
|
+
define_method "#{name}=" do |val|
|
25
|
+
public_send("#{name}_will_change!") unless val == resource_attributes[name] # For Dirty
|
26
|
+
|
27
|
+
attribute = resource_attributes[name]
|
28
|
+
|
29
|
+
unless attribute.valid_with?(value: val)
|
30
|
+
raise ResourceError::AttributeTypeError.new(
|
31
|
+
attribute: attribute,
|
32
|
+
resource: self,
|
33
|
+
value: val
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
attribute.value = val
|
38
|
+
end
|
39
|
+
|
40
|
+
serialize attributes: [name]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def attribute(name, type:)
|
45
|
+
resource_attribute = ResourceAttribute.new(name: name, type: type)
|
46
|
+
|
47
|
+
_define_attribute_methods(name)
|
48
|
+
|
49
|
+
resource_attributes.add(resource_attribute)
|
50
|
+
|
51
|
+
resource_attribute
|
52
|
+
end
|
53
|
+
|
54
|
+
def resource_attributes
|
55
|
+
@resource_attributes ||= ResourceAttributeSet.new(resource: self)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize(**data)
|
60
|
+
# Initialize empty values
|
61
|
+
resource_attributes.keys.each { |e| instance_variable_set("@#{e}", nil) }
|
62
|
+
|
63
|
+
assign_attributes(data)
|
64
|
+
|
65
|
+
super()
|
66
|
+
end
|
67
|
+
|
68
|
+
def assign_attribute(name, value)
|
69
|
+
raise "#{name} is not an attribute of #{self.class.name}" unless resource_attributes.key?(name)
|
70
|
+
|
71
|
+
public_send("#{name}=", value)
|
72
|
+
end
|
73
|
+
|
74
|
+
def assign_attributes(attribute_hash)
|
75
|
+
attribute_hash.each do |name, value|
|
76
|
+
assign_attribute(name, value)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Store attribute instance values separately
|
81
|
+
def resource_attributes
|
82
|
+
@resource_attributes ||= Marshal.load(Marshal.dump(self.class.resource_attributes))
|
83
|
+
end
|
84
|
+
|
85
|
+
def serialize_attributes
|
86
|
+
Hash[resource_attributes.map { |k, v| [k, v.value] }]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../reference'
|
4
|
+
|
5
|
+
# Reference attribute type for has_one
|
6
|
+
module LedgerSync
|
7
|
+
class ResourceAttribute
|
8
|
+
class Reference
|
9
|
+
class Many < Reference
|
10
|
+
module Mixin
|
11
|
+
def self.included(base)
|
12
|
+
base.extend(ClassMethods)
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def references_many(name, to:)
|
17
|
+
resource_attribute = ResourceAttribute::Reference::Many.new(name: name, to: to)
|
18
|
+
resource_attributes.add resource_attribute
|
19
|
+
_define_attribute_methods(name)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(name:, to:)
|
25
|
+
super(
|
26
|
+
name: name,
|
27
|
+
type: Type::ReferenceMany.new(resource_class: to),
|
28
|
+
value: []
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../reference'
|
4
|
+
|
5
|
+
# Reference attribute type for has_one
|
6
|
+
module LedgerSync
|
7
|
+
class ResourceAttribute
|
8
|
+
class Reference
|
9
|
+
class One < Reference
|
10
|
+
module Mixin
|
11
|
+
def self.included(base)
|
12
|
+
base.extend(ClassMethods)
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def references_one(name, to:)
|
17
|
+
resource_attribute = ResourceAttribute::Reference::One.new(name: name, to: to)
|
18
|
+
resource_attributes.add resource_attribute
|
19
|
+
_define_attribute_methods(name)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(name:, to:)
|
25
|
+
super(
|
26
|
+
name: name,
|
27
|
+
type: Type::ReferenceOne.new(resource_class: to)
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LedgerSync
|
4
|
+
class ResourceAttributeSet
|
5
|
+
attr_reader :attributes,
|
6
|
+
:references,
|
7
|
+
:references_one,
|
8
|
+
:references_many,
|
9
|
+
:resource
|
10
|
+
|
11
|
+
delegate :[],
|
12
|
+
:each,
|
13
|
+
:include?,
|
14
|
+
:key?,
|
15
|
+
:keys,
|
16
|
+
:map,
|
17
|
+
to: :attributes
|
18
|
+
|
19
|
+
alias names keys
|
20
|
+
|
21
|
+
def initialize(resource:)
|
22
|
+
@attributes = {}
|
23
|
+
@references = []
|
24
|
+
@references_one = []
|
25
|
+
@references_many = []
|
26
|
+
|
27
|
+
@resource = resource
|
28
|
+
end
|
29
|
+
|
30
|
+
def add(attribute)
|
31
|
+
name = attribute.name
|
32
|
+
raise "Attribute #{name} already exists on #{resource.name}." if attributes.key?(name)
|
33
|
+
|
34
|
+
case attribute
|
35
|
+
when ResourceAttribute
|
36
|
+
nil
|
37
|
+
when ResourceAttribute::Reference::One
|
38
|
+
@references << attribute
|
39
|
+
@references_one << attribute
|
40
|
+
when ResourceAttribute::Reference::Many
|
41
|
+
@references << attribute
|
42
|
+
@references_many << attribute
|
43
|
+
else
|
44
|
+
raise 'Unknown attribute class'
|
45
|
+
end
|
46
|
+
|
47
|
+
@attributes[attribute.name] = attribute
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_a
|
51
|
+
attributes.values
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_h
|
55
|
+
Hash[attributes.map { |k, v| [k, v.value] }]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LedgerSync
|
4
|
+
class Account < LedgerSync::Resource
|
5
|
+
attribute :name, type: Type::String
|
6
|
+
attribute :classification, type: Type::String
|
7
|
+
attribute :account_type, type: Type::String
|
8
|
+
attribute :account_sub_type, type: Type::String
|
9
|
+
attribute :number, type: Type::Integer
|
10
|
+
attribute :currency, type: Type::String
|
11
|
+
attribute :description, type: Type::String
|
12
|
+
attribute :active, type: Type::Boolean
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module LedgerSync
|
2
|
+
class Bill < LedgerSync::Resource
|
3
|
+
attribute :currency, type: Type::String
|
4
|
+
attribute :memo, type: Type::String
|
5
|
+
attribute :transaction_date, type: Type::Date
|
6
|
+
attribute :due_date, type: Type::Date
|
7
|
+
|
8
|
+
references_one :vendor, to: Vendor
|
9
|
+
references_one :account, to: Account
|
10
|
+
|
11
|
+
references_many :line_items, to: BillLineItem
|
12
|
+
|
13
|
+
def name
|
14
|
+
"Bill: #{transaction_date.to_s}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module LedgerSync
|
2
4
|
class Customer < LedgerSync::Resource
|
3
|
-
attribute :email
|
4
|
-
attribute :name
|
5
|
-
attribute :phone_number
|
6
|
-
# attribute :active
|
5
|
+
attribute :email, type: Type::String
|
6
|
+
attribute :name, type: Type::String
|
7
|
+
attribute :phone_number, type: Type::String
|
7
8
|
end
|
8
9
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module LedgerSync
|
2
|
+
class Deposit < LedgerSync::Resource
|
3
|
+
attribute :currency, type: Type::String
|
4
|
+
attribute :memo, type: Type::String
|
5
|
+
attribute :transaction_date, type: Type::Date
|
6
|
+
attribute :exchange_rate, type: Type::Float
|
7
|
+
|
8
|
+
references_one :account, to: Account
|
9
|
+
|
10
|
+
references_many :line_items, to: DepositLineItem
|
11
|
+
|
12
|
+
def name
|
13
|
+
"Deposit: #{currency}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module LedgerSync
|
2
|
+
class Expense < LedgerSync::Resource
|
3
|
+
attribute :currency, type: Type::String
|
4
|
+
attribute :amount, type: Type::Integer
|
5
|
+
attribute :memo, type: Type::String
|
6
|
+
attribute :payment_type, type: Type::String
|
7
|
+
attribute :transaction_date, type: Type::Date
|
8
|
+
attribute :exchange_rate, type: Type::Float
|
9
|
+
|
10
|
+
references_one :vendor, to: Vendor
|
11
|
+
references_one :account, to: Account
|
12
|
+
|
13
|
+
references_many :line_items, to: ExpenseLineItem
|
14
|
+
|
15
|
+
def name
|
16
|
+
"Purchase: #{amount} #{currency}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|