billing 0.0.3 → 0.0.4a

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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/app/models/billing/account.rb +84 -6
  3. data/app/models/billing/charge.rb +15 -0
  4. data/app/models/billing/department.rb +5 -0
  5. data/app/models/billing/modifier.rb +3 -1
  6. data/app/models/billing/operator.rb +4 -0
  7. data/app/models/billing/origin.rb +13 -0
  8. data/app/models/billing/payment.rb +49 -20
  9. data/app/models/billing/payment_external.rb +8 -0
  10. data/app/models/billing/payment_type.rb +3 -1
  11. data/app/models/billing/payment_type_fiscal_driver_mapping.rb +7 -0
  12. data/app/models/billing/payment_with_type.rb +18 -2
  13. data/app/models/billing/paypal_express.rb +7 -0
  14. data/app/models/billing/report.rb +66 -0
  15. data/app/models/billing/version.rb +5 -0
  16. data/app/models/{billing → concerns/billing}/account_item.rb +5 -2
  17. data/config/initializers/paper_trail.rb +3 -0
  18. data/db/migrate/20140721172932_create_billing_operators.rb +11 -0
  19. data/db/migrate/20140721173106_create_billing_departments.rb +12 -0
  20. data/db/migrate/20140722160242_change_table_base_billing_tax_group.rb +12 -0
  21. data/db/migrate/20140722160258_change_table_base_billing_operator.rb +9 -0
  22. data/db/migrate/20140722160529_change_table_base_billing_payment_type.rb +10 -0
  23. data/db/migrate/20140722160902_change_table_base_billing_departments.rb +10 -0
  24. data/db/migrate/20140722161743_change_table_base_billing_plus.rb +15 -0
  25. data/db/migrate/20140722162315_change_table_base_billing_origins.rb +13 -0
  26. data/db/migrate/20140724173527_add_origin_to_billing_account.rb +7 -0
  27. data/db/migrate/20140724173917_remove_origin_and_fiscal_defice_from_billing_payment.rb +6 -0
  28. data/db/migrate/20140725122914_add_status_to_billing_payment.rb +5 -0
  29. data/db/migrate/20140725142525_eqalize_schemas_billing_charge.rb +16 -0
  30. data/db/migrate/20140725143734_eqalize_schemas_billing_department.rb +5 -0
  31. data/db/migrate/20140725145640_eqalize_schemas_billing_closure.rb +25 -0
  32. data/db/migrate/20140725145953_eqalize_schemas_billing_operators.rb +5 -0
  33. data/db/migrate/20140725150329_eqalize_schemas_billing_origin.rb +5 -0
  34. data/db/migrate/20140725151522_eqalize_schemas_billing_payment_type.rb +8 -0
  35. data/db/migrate/20140725152125_eqalize_schemas_billing_payment.rb +14 -0
  36. data/db/migrate/20140725153215_eqalize_schemas_billing_version.rb +16 -0
  37. data/db/migrate/20140725153658_eqalize_schemas_billing_pt_fp_mapping.rb +14 -0
  38. data/db/migrate/20140725164847_add_extface_job_to_billing_account.rb +5 -0
  39. data/db/migrate/20140726163220_rename_closure_to_billing_report.rb +5 -0
  40. data/db/migrate/20140726165718_remove_closure_from_billing_account.rb +6 -0
  41. data/db/migrate/20140726170002_add_report_to_billing_account.rb +5 -0
  42. data/db/migrate/20140728110230_add_autofin_and_finalized_at_to_billing_account.rb +6 -0
  43. data/db/migrate/20140729083050_add_name_to_billing_account.rb +5 -0
  44. data/db/migrate/20140729083408_add_number_to_billing_account.rb +5 -0
  45. data/db/migrate/20140729180849_add_deleted_at_to_billing_modifier.rb +5 -0
  46. data/db/migrate/20140729182146_add_deleted_at_to_billing_account.rb +5 -0
  47. data/lib/billing/billable.rb +6 -2
  48. data/lib/billing/engine.rb +1 -0
  49. data/lib/billing/version.rb +1 -1
  50. data/test/dummy/db/development.sqlite3 +0 -0
  51. data/test/dummy/db/schema.rb +139 -1
  52. data/test/dummy/db/test.sqlite3 +0 -0
  53. data/test/dummy/log/development.log +5698 -0
  54. data/test/dummy/log/test.log +63958 -0
  55. data/test/dummy/tmp/cache/assets/test/sprockets/0cab887466527f5353fd43adab72769b +0 -0
  56. data/test/dummy/tmp/cache/assets/test/sprockets/79316f9f3f5ebcec9b368d92d2e97e23 +0 -0
  57. data/test/dummy/tmp/cache/assets/test/sprockets/d3ab844c61f1c211ed5fad5407cd18ef +0 -0
  58. data/test/fixtures/billing/accounts.yml +2 -0
  59. data/test/fixtures/billing/departments.yml +11 -0
  60. data/test/fixtures/billing/modifiers.yml +1 -0
  61. data/test/fixtures/billing/operators.yml +11 -0
  62. data/test/fixtures/billing/origins.yml +2 -0
  63. data/test/fixtures/billing/payment_types.yml +16 -0
  64. data/test/fixtures/billing/payments.yml +5 -1
  65. data/test/models/billing/account_test.rb +19 -6
  66. data/test/models/billing/department_test.rb +9 -0
  67. data/test/models/billing/modifier_test.rb +13 -0
  68. data/test/models/billing/operator_test.rb +9 -0
  69. data/test/models/billing/payment_test.rb +25 -0
  70. metadata +64 -6
  71. data/app/assets/javascripts/billing/payments.js +0 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 747d5529859b70e9cc69f11653785d07d55b74a4
4
- data.tar.gz: cfc4af81e2d5e2361a2bea947e7e2bb3cdc251f2
3
+ metadata.gz: b846f68aa64260bdc6f155667d462d2aebd32cab
4
+ data.tar.gz: b4f066fa781ea284213e6e62011406ac2590d1b3
5
5
  SHA512:
6
- metadata.gz: c6c723a641929e19b86cd4bbfd1e175606036f055ba0112691c82c5e573b08bbc411133c6c577aef41655859e85a100a26b5ed60ed3c6e7bd8a6f3c6d9b7f535
7
- data.tar.gz: c6d5372d0418f727050175f5eddf78c67f2cf903545722a593e4c9a6bb2bab8342c769cfd9a4d21a3741ed40053dfa4d3d341483ad157606e83c3381b72bebdf
6
+ metadata.gz: 732df67d2e5ff40579b1c508841cdd9873de3b6ce0c48ce622f11d3aac7cf1903a6d44152d7d3fbede4d8045df0a00015c7652ae0c7226df9132e6dc0ffe11b5
7
+ data.tar.gz: b3a9f712e2116801391bf16be73502d0a56a36b44bd3fa4c9115be7036a4e6d03d81cf6f21cd328f31ca53c917b14967be05959840d1a7ccf81a19ed1fbb1841
@@ -1,5 +1,8 @@
1
1
  module Billing
2
2
  class Account < ActiveRecord::Base
3
+ acts_as_paranoid if respond_to?(:acts_as_paranoid)
4
+ has_paper_trail class_name: 'Billing::Version' if respond_to?(:has_paper_trail)
5
+
3
6
  monetize :charges_sum_cents
4
7
  monetize :discounts_sum_cents
5
8
  monetize :surcharges_sum_cents
@@ -8,15 +11,38 @@ module Billing
8
11
  monetize :balance_cents
9
12
 
10
13
  belongs_to :billable, polymorphic: true
11
- has_many :charges, inverse_of: :account
12
- has_many :modifiers, inverse_of: :account
13
- has_many :payments, inverse_of: :account
14
+ has_many :charges, inverse_of: :account, dependent: :destroy
15
+ has_many :modifiers, inverse_of: :account, dependent: :destroy
16
+ has_many :payments, inverse_of: :account, dependent: :restrict_with_error
17
+ belongs_to :origin, inverse_of: :accounts
18
+ belongs_to :report, inverse_of: :accounts
19
+
20
+ if defined? Extface
21
+ belongs_to :extface_job, class_name: 'Extface::Job'
22
+ end
14
23
 
15
24
  accepts_nested_attributes_for :charges, :modifiers, :payments
16
25
 
26
+ delegate :payment_model, to: :origin, prefix: :origin, allow_nil: true
27
+
28
+ scope :unpaid, -> { where(arel_table[:balance_cents].lt(0)) }
29
+ scope :open, -> { where.not(balance_cents: 0) }
30
+ scope :partially_paid, -> { where.not( payments_sum_cents: 0, balance_cents: 0 ) }
31
+
17
32
  before_validation :update_sumaries
18
33
 
34
+ before_save on: :create do
35
+ self.number = "#{billable.id}:#{billable.billing_accounts.count}"
36
+ self.name = "B:#{number}" if name.nil?
37
+ end
38
+ before_save :perform_autofin, if: :becomes_paid?
39
+
19
40
  validates_numericality_of :total, greater_than_or_equal_to: 0
41
+ validates_numericality_of :balance, less_than_or_equal_to: 0
42
+ validates_presence_of :origin, if: :has_payments?
43
+
44
+ validates_absence_of :payments_of_diff_models?,
45
+ :payments_of_diff_fiscalization?, :multiple_cash_payments?, if: :has_payments?
20
46
 
21
47
  def charge(*args)
22
48
  c = charges.new Charge.args(*args)
@@ -29,7 +55,7 @@ module Billing
29
55
  end
30
56
 
31
57
  def pay(*args)
32
- p = payments.new Payment.args(*args)
58
+ p = build_typed_payment Payment.args(*args)
33
59
  p if p.save
34
60
  end
35
61
 
@@ -38,7 +64,28 @@ module Billing
38
64
  end
39
65
 
40
66
  def payment_types
41
- billable.try(:billing_payment_types) || billable.try(:payment_types) || []
67
+ billable.try(:billing_payment_types) || Billing::PaymentType.all
68
+ end
69
+
70
+ def origins
71
+ billable.try(:billing_origins) || Billing::Origin.all
72
+ end
73
+
74
+ def has_payments?
75
+ payments.any?
76
+ end
77
+
78
+ def paid?
79
+ has_payments? && balance.zero?
80
+ end
81
+
82
+ def build_typed_payment(attributes = {})
83
+ payments.new(attributes.merge(type: origin.try(:payment_model) || 'Billing::PaymentWithType'))
84
+ end
85
+
86
+ def fiscalize #TODO test
87
+ extface_job = origin.fiscal_device.fiscalize(self) if fiscalizable? && origin.try(:fiscal_device)
88
+ extface_job if save
42
89
  end
43
90
 
44
91
  private
@@ -46,7 +93,8 @@ module Billing
46
93
  @modifier_items = ModifierItems.new.tap() do |items|
47
94
  modifiers.each do |modifier|
48
95
  if charge = modifier.charge
49
- items << Charge.new(price: modifier.percent_ratio.nil? ? modifier.fixed_value : (charge.price * modifier.percent_ratio), chargable: charge)
96
+ charge.value = modifier.percent_ratio.nil? ? modifier.fixed_value : (charge.price * modifier.percent_ratio)
97
+ items << Charge.new(price: charge.value, chargable: charge)
50
98
  else
51
99
  items << Charge.new(price: modifier.percent_ratio.nil? ? modifier.fixed_value : (charges.to_a.sum(&:price).to_money * modifier.percent_ratio))
52
100
  end
@@ -62,5 +110,35 @@ module Billing
62
110
  self.total = charges_sum + surcharges_sum - discounts_sum
63
111
  self.balance = payments_sum - total
64
112
  end
113
+
114
+ def payments_of_diff_models?
115
+ payments.to_a.group_by(&:type).many?
116
+ end
117
+
118
+ def payments_of_diff_fiscalization?
119
+ payments.to_a.group_by(&:fiscal?).many?
120
+ end
121
+
122
+ def multiple_cash_payments?
123
+ payments.to_a.select(&:cash?).many?
124
+ end
125
+
126
+ def becomes_paid?
127
+ finalized_at.nil? && has_payments? && balance.zero?
128
+ end
129
+
130
+ def perform_autofin
131
+ if autofin
132
+ self.finalized_at = Time.now
133
+ if defined? Extface
134
+ self.extface_job = origin.fiscal_device.fiscalize(self) if fiscalizable? && origin.try(:fiscal_device)
135
+ end
136
+ end
137
+ true
138
+ end
139
+
140
+ def fiscalizable?
141
+ payments.select(&:fiscal?).any?
142
+ end
65
143
  end
66
144
  end
@@ -3,9 +3,24 @@ module Billing
3
3
  include AccountItem
4
4
  belongs_to :account, inverse_of: :charges, validate: true
5
5
  belongs_to :chargable, polymorphic: true
6
+ belongs_to :origin, inverse_of: :charges
7
+ has_one :modifier, inverse_of: :charge
8
+
6
9
  monetize :price_cents
10
+ monetize :value_cents
11
+
12
+ delegate :paid?, to: :account
13
+
14
+ scope :unpaid, -> { joins(:account).where.not(billing_accounts: { balance_cents: 0}) }
15
+ scope :paid, -> { joins(:account).where(billing_accounts: { balance_cents: 0}) }
16
+ scope :in_period, lambda {|from, to| where(revenue_at: from..to) }
7
17
 
8
18
  validates_presence_of :price
19
+ validates_numericality_of :value, greater_than_or_equal_to: 0
20
+
21
+ before_save do
22
+ self.value = price unless modifier.present? #FIXME global account modifier lost
23
+ end
9
24
 
10
25
  class << self
11
26
  def args(*args)
@@ -0,0 +1,5 @@
1
+ module Billing
2
+ class Department < ActiveRecord::Base
3
+ belongs_to :tax_group
4
+ end
5
+ end
@@ -2,10 +2,12 @@ module Billing
2
2
  class Modifier < ActiveRecord::Base
3
3
  include AccountItem
4
4
  belongs_to :account, inverse_of: :modifiers, validate: true
5
- belongs_to :charge
5
+ belongs_to :charge, inverse_of: :modifier
6
6
  monetize :fixed_value_cents
7
7
 
8
8
  validate :percent_or_value
9
+ validates_uniqueness_of :charge, scope: :account_id, allow_nil: true
10
+ validates_uniqueness_of :account, scope: :charge_id
9
11
 
10
12
  private
11
13
  def percent_or_value
@@ -0,0 +1,4 @@
1
+ module Billing
2
+ class Operator < ActiveRecord::Base
3
+ end
4
+ end
@@ -1,4 +1,17 @@
1
1
  module Billing
2
2
  class Origin < ActiveRecord::Base
3
+ has_many :accounts, inverse_of: :origin
4
+ has_many :charges, inverse_of: :origin
5
+ has_many :payments, through: :accounts
6
+ if defined? Extface
7
+ belongs_to :fiscal_device, ->(o) { where( extfaceable_id: o.master_id ) }, class_name: 'Extface::Device'
8
+ end
9
+
10
+ validates_presence_of :name
11
+ validates :payment_model, inclusion: { in: Payment::PAYMENT_MODELS }
12
+
13
+ def external_payment?
14
+ payment_model != Payment::PAYMENT_WITH_TYPE
15
+ end
3
16
  end
4
17
  end
@@ -1,39 +1,68 @@
1
1
  module Billing
2
2
  class Payment < ActiveRecord::Base
3
+ PAYMENT_WITH_TYPE = 'Billing::PaymentWithType'.freeze
4
+ PAYMENT_EXTERNAL = 'Billing::PaymentExternal'.freeze
5
+ PAYPAL_EXPRESS = 'Billing::PaypalExpress'.freeze
6
+ PAYMENT_MODELS = [PAYMENT_WITH_TYPE, PAYMENT_EXTERNAL, PAYPAL_EXPRESS].freeze
7
+
3
8
  include AccountItem
4
- belongs_to :account, inverse_of: :payments, validate: true
5
9
 
10
+ attr_writer :origin
11
+ attr_accessor :origin_id
6
12
  monetize :value_cents
13
+
14
+ belongs_to :account, inverse_of: :payments, validate: true
7
15
 
8
- delegate :billable, to: :account
16
+ scope :in_period, lambda {|from, to| where(created_at: from..to) }
17
+ scope :for_report, -> { joins(:account).where(billing_accounts: { balance_cents: 0 ,report_id: nil }) }
9
18
 
10
- validates_presence_of :value
19
+ if defined? Extface
20
+ belongs_to :extface_job, class_name: 'Extface::Job'
21
+ end
22
+
23
+ delegate :billable, to: :account
24
+
25
+ validates_numericality_of :value, greater_than_or_equal_to: 0
26
+ validates :type, inclusion: { in: PAYMENT_MODELS }
11
27
 
12
28
  after_initialize on: :create do
13
- self.value = -account.try(:balance).to_money if value.zero?
29
+ self.value = -account.try(:balance) if value.zero?
14
30
  end
15
31
 
16
- class << self
17
- def args(*args)
18
- h = { type: 'Billing::PaymentWithType' }
19
- case when args.blank? || args.first.kind_of?(Hash) then
20
- args.blank? ? h : h.merge(*args)
21
- when args.first.kind_of?(String) then
22
- #TODO parse
23
- else
24
- h.merge!(payment_type_id: args.shift.to_param)
25
- if args.any? && (args.first.kind_of?(Hash) || args.first.kind_of?(String))
26
- h.merge(args(*args))
32
+ before_validation do
33
+ account.origin = origin unless account.origin and account.payments.many?
34
+ end
35
+
36
+ def fiscal?; false; end
37
+ def cash?; false; end
38
+ def external?; false; end
39
+
40
+ def origin
41
+ @origin || origins.find_by_id(@origin_id)
42
+ end
43
+
44
+ private
45
+ class << self
46
+ def args(*args)
47
+ h = {}
48
+ case when args.blank? || args.first.kind_of?(Hash) then
49
+ args.blank? ? h : h.merge(*args)
50
+ when args.first.kind_of?(String) then
51
+ #TODO parse
27
52
  else
28
- if args.blank?
29
- h
53
+ h.merge!(payment_type_id: args.shift.to_param)
54
+ if args.any? && (args.first.kind_of?(Hash) || args.first.kind_of?(String))
55
+ h.merge(args(*args))
30
56
  else
31
- h.merge!( value: args.shift.to_money )
32
- args.any? ? h.merge(*args) : h
57
+ if args.blank?
58
+ h
59
+ else
60
+ h.merge!( value: args.shift.to_money )
61
+ args.any? ? h.merge(*args) : h
62
+ end
33
63
  end
34
64
  end
35
65
  end
36
66
  end
37
- end
38
67
  end
39
68
  end
@@ -0,0 +1,8 @@
1
+ module Billing
2
+ class PaymentExternal < Payment
3
+ validates_presence_of :external_token
4
+
5
+ def external?; true; end
6
+
7
+ end
8
+ end
@@ -1,6 +1,8 @@
1
1
  module Billing
2
2
  class PaymentType < ActiveRecord::Base
3
- has_many :payments
3
+ has_many :payments, inverse_of: :payment_type
4
+ has_many :payment_type_fiscal_driver_mappings, inverse_of: :payment_type
5
+
4
6
  validates_presence_of :name
5
7
  end
6
8
  end
@@ -0,0 +1,7 @@
1
+ module Billing
2
+ class PaymentTypeFiscalDriverMapping < ActiveRecord::Base
3
+ self.table_name = "billing_pt_fp_mappings"
4
+ belongs_to :payment_type, inverse_of: :payment_type_fiscal_driver_mappings
5
+ belongs_to :extface_driver, class_name: 'Extface::Driver'
6
+ end
7
+ end
@@ -1,14 +1,30 @@
1
1
  module Billing
2
2
  class PaymentWithType < Payment
3
3
  belongs_to :payment_type, inverse_of: :payments
4
- delegate :payment_types, to: :account
5
4
 
6
5
  validates_presence_of :payment_type
7
6
  validates :payment_type, inclusion: { in: :payment_types }
8
7
 
9
8
  after_initialize on: :create do
10
- self.payment_type = billable.try(:default_payment_type) unless payment_type
9
+ self.payment_type = default_payment_type unless payment_type
11
10
  end
12
11
 
12
+ def fiscal?
13
+ payment_type.fiscal
14
+ end
15
+
16
+ def cash?
17
+ payment_type.cash
18
+ end
19
+
20
+ private
21
+ def default_payment_type
22
+ if pt = billable.try(:default_payment_type)
23
+ pt
24
+ else
25
+ account.payment_types.try(:first) unless account.payment_types.many?
26
+ end
27
+ end
28
+
13
29
  end
14
30
  end
@@ -0,0 +1,7 @@
1
+ module Billing
2
+ class PaypalExpress < Payment
3
+
4
+ def paypal_express?; true; end
5
+
6
+ end
7
+ end
@@ -0,0 +1,66 @@
1
+ module Billing
2
+ class Report < ActiveRecord::Base
3
+ FISCAL_X_REPORT = 'x_report'.freeze
4
+ FISCAL_Z_REPORT = 'z_report'.freeze
5
+ FISCAL_PERIOD_REPORT = 'period_report'.freeze
6
+ F_OPERATIONS = [FISCAL_X_REPORT, FISCAL_Z_REPORT, FISCAL_PERIOD_REPORT].freeze
7
+
8
+ has_paper_trail class_name: 'Billing::Version'
9
+ belongs_to :origin, inverse_of: :reports
10
+ has_many :accounts, inverse_of: :report, autosave: true
11
+ belongs_to :extface_job, class_name: 'Extface::Job'
12
+ has_many :payments, through: :accounts
13
+
14
+ monetize :payments_sum_cents
15
+ monetize :payments_cash_cents
16
+ monetize :payments_fiscal_cents
17
+
18
+ validates_presence_of :origin
19
+ validates_absence_of :partially_paid_accounts?
20
+ validates :f_operation, inclusion: { in: F_OPERATIONS }, allow_nil: true
21
+ validates_presence_of :f_period_from, :f_period_to, if: :fiscal_period_report?
22
+
23
+ before_validation :set_report_to_accounts
24
+ before_create :update_summary
25
+
26
+
27
+ def fiscalization
28
+ if origin.fiscal_device.present?
29
+ perform_fiscal_job
30
+ save
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def fiscal_period_report?
37
+ f_operation == FISCAL_PERIOD_REPORT
38
+ end
39
+
40
+ def set_report_to_accounts
41
+ self.accounts << origin.accounts.select(&:paid?) if zeroing?
42
+ end
43
+
44
+ def update_summary
45
+ self.payments_sum = accounts.to_a.sum(Money.new(0, 'USD'), &:payments_sum)
46
+ self.payments_cash = payments.select{ |p| p.try(:cash?) }.sum(Money.new(0, 'USD'), &:value)
47
+ self.payments_fiscal = payments.select{ |p| p.try(:fiscal?) }.sum(Money.new(0, 'USD'), &:value)
48
+ perform_fiscal_job
49
+ end
50
+
51
+ def perform_fiscal_job
52
+ case f_operation
53
+ when FISCAL_Z_REPORT then
54
+ self.extface_job = origin.fiscal_device.driver.z_report_session
55
+ when FISCAL_X_REPORT then
56
+ self.extface_job = origin.fiscal_device.driver.x_report_session
57
+ when FISCAL_PERIOD_REPORT then
58
+ p "period"
59
+ end
60
+ end
61
+
62
+ def partially_paid_accounts?
63
+ accounts.partially_paid.any?
64
+ end
65
+ end
66
+ end