billing 0.0.3 → 0.0.4a

Sign up to get free protection for your applications and to get access to all the features.
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