invoicing_payments_processing 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ module BlackStack
2
+ class InvoiceItem < Sequel::Model(:invoice_item)
3
+ InvoiceItem.dataset = InvoiceItem.dataset.disable_insert_output
4
+ many_to_one :invoice, :class=>:'BlackStack::Invoice', :key=>:id_invoice
5
+
6
+ def plan_descriptor()
7
+ BlackStack::InvoicingPaymentsProcessing::plan_descriptor(self.item_number)
8
+ end
9
+
10
+ end # class LocalInvoiceItem
11
+ end # module BlackStack
@@ -0,0 +1,127 @@
1
+ require 'blackstack_commons'
2
+ require 'pampa_workers'
3
+
4
+ #
5
+ #
6
+ #
7
+ module BlackStack
8
+
9
+ module InvoicingPaymentsProcessing
10
+
11
+ # constants
12
+ PAYPAL_ORDERS_URL = "https://www.paypal.com"
13
+
14
+ # static attributes
15
+ @@paypal_business_email = "sardi.leandro.daniel@gmail.com"
16
+ @@products_descriptor = []
17
+ @@plans_descriptor = []
18
+
19
+ # getters & setters
20
+ def self.set_paypal_business_email(email)
21
+ @@paypal_business_email = email
22
+ end # def self.set_paypal_business_email
23
+
24
+ def self.paypal_business_email()
25
+ @@paypal_business_email
26
+ end # def self.set_paypal_business_email
27
+
28
+ def self.paypal_ipn_listener()
29
+ "#{BlackStack::Pampa::api_url.to_s}/api1.3/accounting/paypal/notify_new_invoice.json"
30
+ end
31
+
32
+
33
+ def self.set_products(h)
34
+ @@products_descriptor = h
35
+ end # def self.set_products
36
+
37
+ def self.products_descriptor()
38
+ @@products_descriptor
39
+ end # def self.products_descriptor
40
+
41
+
42
+ def self.set_plans(h)
43
+ @@plans_descriptor = h
44
+ end # def self.set_plans
45
+
46
+ def self.plans_descriptor()
47
+ @@plans_descriptor
48
+ end # def self.plans_descriptor
49
+
50
+
51
+ def self.plan_descriptor(item_number)
52
+ plan = BlackStack::InvoicingPaymentsProcessing::plans_descriptor.select { |h| h[:item_number].to_s == item_number.to_s }.first
53
+ raise "Plan not found" if plan.nil?
54
+ plan
55
+ end
56
+
57
+ def self.product_descriptor(product_code)
58
+ ret = BlackStack::InvoicingPaymentsProcessing::products_descriptor.select { |h| h[:code] == product_code }.first
59
+ raise "Product not found" if ret.nil?
60
+ ret
61
+ end
62
+
63
+
64
+ def self.require_db_classes()
65
+ # You have to load all the Sinatra classes after connect the database.
66
+ require_relative '../lib/balance.rb'
67
+ require_relative '../lib/bufferpaypalnotification.rb'
68
+ require_relative '../lib/customplan.rb'
69
+ require_relative '../lib/invoice.rb'
70
+ require_relative '../lib/invoiceitem.rb'
71
+ require_relative '../lib/movement.rb'
72
+ require_relative '../lib/paypalsubscription.rb'
73
+ require_relative '../lib/extend_client_by_invoicing_payments_processing.rb'
74
+ end
75
+
76
+ class BasePlan
77
+ PAYMENT_PAY_AS_YOU_GO = 'G'
78
+ PAYMENT_SUBSCRIPTION = 'S'
79
+
80
+ CONSUMPTION_BY_UNIT = 0 # el producto se conume credito por credito. Ejemplo: lead records.
81
+ CONSUMPTION_BY_TIME = 1 # el producto exira al final del periodo de la factura. Ejemplos: Publicidad. Membresía.
82
+
83
+ PRODUCT_WAREHOUSE = 'Warehouse Service'
84
+ PRODUCT_SOFTWARE = 'Software Service'
85
+ PRODUCT_AGENCY = 'Agency Service'
86
+ PRODUCT_EDUCATION = 'Education Service'
87
+ PRODUCT_OTHER = 'Other Service'
88
+
89
+ #
90
+ def self.payment_types()
91
+ [PAYMENT_PAY_AS_YOU_GO, PAYMENT_SUBSCRIPTION]
92
+ end
93
+
94
+ def self.payment_type_description(type)
95
+ return 'Pay as You Go' if type == PAYMENT_PAY_AS_YOU_GO
96
+ return 'Subscription' if type == PAYMENT_SUBSCRIPTION
97
+ end
98
+
99
+ #
100
+ def self.consumption_types()
101
+ [CONSUMPTION_BY_UNIT, CONSUMPTION_BY_TIME]
102
+ end
103
+
104
+ def self.consumption_type_description(type)
105
+ return 'Pay as You Go' if type == CONSUMPTION_BY_UNIT
106
+ return 'Subscription' if type == CONSUMPTION_BY_TIME
107
+ end
108
+
109
+ #
110
+ def self.product_types()
111
+ [PRODUCT_WAREHOUSE, PRODUCT_SOFTWARE, PRODUCT_AGENCY, PRODUCT_EDUCATION, PRODUCT_OTHER]
112
+ end
113
+
114
+ def self.product_type_icon(s)
115
+ return "icon-cloud" if s == PRODUCT_WAREHOUSE
116
+ return "icon-desktop" if s == PRODUCT_SOFTWARE
117
+ return "icon-coffee" if s == PRODUCT_AGENCY
118
+ return "icon-book" if s == PRODUCT_EDUCATION
119
+ return "icon-help" if s == PRODUCT_OTHER
120
+ end
121
+
122
+
123
+ end # class BasePlan
124
+
125
+ end # module InvoicingPaymentsProcessing
126
+
127
+ end # module BlackStack
@@ -0,0 +1,138 @@
1
+ module BlackStack
2
+ class Movement < Sequel::Model(:movement)
3
+ BlackStack::Movement.dataset = BlackStack::Movement.dataset.disable_insert_output
4
+ self.dataset = self.dataset.disable_insert_output
5
+
6
+ many_to_one :invoiceItem, :class=>:'BlackStack::InvoiceItem', :key=>:id_invoice_item
7
+ many_to_one :client, :class=>:'BlackStack::Client', :key=>:id_client
8
+
9
+ MOVEMENT_TYPE_ADD_PAYMENT = 0
10
+ MOVEMENT_TYPE_ADD_BONUS = 1
11
+ MOVEMENT_TYPE_REASSIGN_BALANCE = 2
12
+ MOVEMENT_TYPE_REFUND_BALANCE = 3
13
+ MOVEMENT_TYPE_CANCELATION = 4 # liability with the client is reduced due service delivery
14
+ MOVEMENT_TYPE_EXPIRATION = 5 # liability with the client is reduced due credits expiration
15
+
16
+ def typeName()
17
+ if (self.type==MOVEMENT_TYPE_ADD_PAYMENT)
18
+ return "Payment"
19
+ elsif (self.type==MOVEMENT_TYPE_ADD_BONUS)
20
+ return "Bonus"
21
+ elsif (self.type==MOVEMENT_TYPE_REASSIGN_BALANCE)
22
+ return "Reassignation"
23
+ elsif (self.type==MOVEMENT_TYPE_REFUND_BALANCE)
24
+ return "Refund"
25
+ elsif (self.type==MOVEMENT_TYPE_CANCELATION)
26
+ return "Service"
27
+ elsif (self.type==MOVEMENT_TYPE_EXPIRATION)
28
+ return "Expiration"
29
+ end
30
+ end
31
+
32
+ def typeColorName()
33
+ if (self.type==MOVEMENT_TYPE_ADD_PAYMENT)
34
+ return "green"
35
+ elsif (self.type==MOVEMENT_TYPE_ADD_BONUS)
36
+ return "orange"
37
+ elsif (self.type==MOVEMENT_TYPE_REASSIGN_BALANCE)
38
+ return "black"
39
+ elsif (self.type==MOVEMENT_TYPE_REFUND_BALANCE)
40
+ return "red"
41
+ elsif (self.type==MOVEMENT_TYPE_CANCELATION)
42
+ return "blue"
43
+ elsif (self.type==MOVEMENT_TYPE_EXPIRATION)
44
+ return "blue"
45
+ end
46
+ end
47
+
48
+ # actualiza el registro con los valores del item de una factura
49
+ # type may be either MOVEMENT_TYPE_ADD_PAYMENT or MOVEMENT_TYPE_ADD_BONUS, but not other value
50
+ def parse(item, type=MOVEMENT_TYPE_ADD_PAYMENT, description='n/a')
51
+ plan = BlackStack::InvoicingPaymentsProcessing.plan_descriptor(item.item_number)
52
+ prod = BlackStack::InvoicingPaymentsProcessing.product_descriptor(plan[:product_code])
53
+
54
+ if (self.id==nil)
55
+ self.id=guid()
56
+ end
57
+ self.id_client = item.invoice.id_client
58
+ self.id_invoice_item = item.id
59
+ self.create_time = item.invoice.billing_period_from
60
+ self.type = type
61
+ if (type != MOVEMENT_TYPE_ADD_BONUS)
62
+ self.paypal1_amount = item.amount.to_f
63
+ self.bonus_amount = 0
64
+ else
65
+ self.paypal1_amount = 0
66
+ self.bonus_amount = item.amount.to_f
67
+ end
68
+ self.amount = 0-item.amount.to_f
69
+ self.credits = 0-item.units.to_i
70
+ self.product_code = item.product_code
71
+ self.profits_amount = 0
72
+ self.description = description
73
+ self.expiration_time = DB["SELECT DATEADD(#{prod[:credits_expiration_period].to_s}#{prod[:credits_expiration_period].to_s}, +#{prod[:credits_expiration_units].to_s}, GETDATE()) AS d"].first[:d].to_s
74
+ self.save()
75
+ end
76
+
77
+ # Creates a new record in the table movement, as an expiration of the unused credits of this movement.
78
+ # This movement must be either a payment or a bonus.
79
+ # This movement must have an expiration time.
80
+ # The expiraton time of this movement is lower than the current date-time.
81
+ def expire()
82
+ m = self
83
+ p = BlackStack::InvoicingPaymentsProcessing::product_descriptor(m.product_code)
84
+
85
+ if m.type != MOVEMENT_TYPE_ADD_PAYMENT && m.type != MOVEMENT_TYPE_ADD_BONUS
86
+ raise "Movement type mismatch."
87
+ end
88
+
89
+ if m.expiration_time.nil?
90
+ raise "Expiration time is null."
91
+ end
92
+
93
+ if m.expiration_time >= Time::now()
94
+ raise "Expiration time is pending."
95
+ end
96
+
97
+ if m.expiration_time <= m.create_time
98
+ raise "Expiration time is lower then creation time."
99
+ end
100
+
101
+ # calculo cuantos creditos tiene este cliente
102
+ x = 0.to_f - BlackStack::Balance.new(m.client.id, p[:code]).credits.to_f
103
+
104
+ # calculo el credito adquirido luego de este movimiento que voy a cancelar
105
+ y = m.client.movements.select { |o|
106
+ (
107
+ o.type == BlackStack::Movement::MOVEMENT_TYPE_ADD_PAYMENT ||
108
+ o.type == BlackStack::Movement::MOVEMENT_TYPE_ADD_BONUS
109
+ ) &&
110
+ o.product_code.upcase == p[:code].upcase &&
111
+ o.create_time > m.create_time
112
+ }.inject(0) { |sum, o| sum.to_f + (0.to_f - o.credits.to_f) }.to_f
113
+
114
+ # calculo el # de creditos no usados de este movimiento que ha expirado
115
+ z = x-y>0 ? x-y : 0
116
+
117
+ # si el monto expirado es positivo, entonces registro
118
+ # una cancelacion de saldo
119
+ if z>0
120
+ amount = ( m.amount.to_f / m.credits.to_f ) * z.to_f
121
+ m = BlackStack::Movement.new(
122
+ :id_client => m.client.id,
123
+ :create_time => now(),
124
+ :type => BlackStack::Movement::MOVEMENT_TYPE_EXPIRATION,
125
+ :description => "Expiration of #{m.id.to_guid}",
126
+ :paypal1_amount => 0,
127
+ :bonus_amount => 0,
128
+ :amount => amount,
129
+ :credits => z,
130
+ :profits_amount => -amount,
131
+ :product_code => p[:code],
132
+ )
133
+ m.id = guid()
134
+ m.save
135
+ end # z>0
136
+ end # def expire
137
+ end # class Movement
138
+ end # module BlackStack
@@ -0,0 +1,131 @@
1
+ module BlackStack
2
+ class PayPalSubscription < Sequel::Model(:payPal_subscription)
3
+ BlackStack::PayPalSubscription.dataset = BlackStack::PayPalSubscription.dataset.disable_insert_output
4
+ many_to_one :buffer_paypal_notification, :class=>:'BlackStack::BufferPayPalNotification', :key=>:id_buffer_paypal_notification
5
+ many_to_one :client, :class=>:'BlackStack::Client', :key=>:id_client
6
+ one_to_many :invoices, :class=>:'BlackStack::Invoice', :key=>:id_paypal_subscription
7
+
8
+ # -----------------------------------------------------------------------------------------
9
+ # Factory
10
+ # -----------------------------------------------------------------------------------------
11
+ #
12
+
13
+ # retorna true si existe un registro en :payPal_subscription con el mismo valor subscr_id.
14
+ # sin retorna false.
15
+ def self.load(params)
16
+ BlackStack::PayPalSubscription.where(:subscr_id=>params['subscr_id']).first
17
+ end
18
+
19
+ # crea un nuevo objeto BufferPayPalNotification, y le mapea los atributos en el hash params.
20
+ # no guarda el objeto en la base de datos.
21
+ # retorna el objeto creado.
22
+ def self.create(params)
23
+ s = BlackStack::PayPalSubscription.new()
24
+ s.subscr_id = params['subscr_id'].to_s
25
+ s.last_name = params['last_name'].to_s
26
+ s.residence_country = params['residence_country'].to_s
27
+ s.mc_currency = params['mc_currency'].to_s
28
+ s.item_name = params['item_name'].to_s
29
+ s.amount1 = params['amount1'].to_s
30
+ s.business = params['business'].to_s
31
+ s.amount3 = params['amount3'].to_s
32
+ s.recurring = params['recurring'].to_s
33
+ s.verify_sign = params['verify_sign'].to_s
34
+ s.payer_status = params['payer_status'].to_s
35
+ s.test_ipn = params['test_ipn'].to_s
36
+ s.payer_email = params['payer_email'].to_s
37
+ s.first_name = params['first_name'].to_s
38
+ s.receiver_email = params['receiver_email'].to_s
39
+ s.payer_id = params['payer_id'].to_s
40
+ s.invoice = params['invoice'].to_s
41
+ s.reattempt = params['reattempt'].to_s
42
+ s.item_number = params['item_number'].to_s
43
+ s.subscr_date = params['subscr_date'].to_s
44
+ s.charset = params['charset'].to_s
45
+ s.notify_version = params['notify_version'].to_s
46
+ s.period1 = params['period1'].to_s
47
+ s.mc_amount1 = params['mc_amount1'].to_s
48
+ s.period3 = params['period3'].to_s
49
+ s.mc_amount3 = params['mc_amount3'].to_s
50
+ s.ipn_track_id = params['ipn_track_id'].to_s
51
+ s
52
+ end
53
+
54
+ # retorna un hash descriptor de este objecto
55
+ def to_hash()
56
+ ret = {}
57
+ # campos de uso interno
58
+ ret['id'] = self.id
59
+ ret['create_time'] = self.create_time.datetime_sql_to_api
60
+ ret['id_client'] = self.id_client
61
+ ret['id_buffer_paypal_notification'] = self.id_buffer_paypal_notification
62
+ ret['active'] = self.active
63
+ # vinculacion a objetos
64
+ ret['id_pipeline'] = self.id_pipeline
65
+ ret['id_lngroup'] = self.id_lngroup
66
+ ret['id_crmlist'] = self.id_crmlist
67
+ # campos replicados del ipn
68
+ ret['subscr_id'] = self.subscr_id
69
+ ret['last_name'] = self.last_name
70
+ ret['residence_country'] = self.residence_country
71
+ ret['mc_currency'] = self.mc_currency
72
+ ret['item_name'] = self.item_name
73
+ ret['amount1'] = self.amount1
74
+ ret['business'] = self.business
75
+ ret['amount3'] = self.amount3
76
+ ret['recurring'] = self.recurring
77
+ ret['verify_sign'] = self.verify_sign
78
+ ret['payer_status'] = self.payer_status
79
+ ret['test_ipn'] = self.test_ipn
80
+ ret['payer_email'] = self.payer_email
81
+ ret['first_name'] = self.first_name
82
+ ret['receiver_email'] = self.receiver_email
83
+ ret['payer_id'] = self.payer_id
84
+ ret['invoice'] = self.invoice
85
+ ret['reattempt'] = self.reattempt
86
+ ret['item_number'] = self.item_number
87
+ ret['subscr_date'] = self.subscr_date
88
+ ret['charset'] = self.charset
89
+ ret['notify_version'] = self.notify_version
90
+ ret['period1'] = self.period1
91
+ ret['mc_amount1'] = self.mc_amount1
92
+ ret['period3'] = self.period3
93
+ ret['mc_amount3'] = self.mc_amount3
94
+ ret['ipn_track_id'] = self.ipn_track_id
95
+ ret
96
+ end
97
+
98
+ # TODO: deprecated
99
+ # Retorna el objeto vinculado a esta suscripcion
100
+ def assignedObject()
101
+ if (self.item_number =~ /^#{PRODUCT_SSM}\./)
102
+ return self.pipeline
103
+ elsif (self.item_number =~ /^#{PRODUCT_FLD}\./)
104
+ return self.crmlist
105
+ elsif (self.item_number =~ /^#{PRODUCT_IPJ}\./)
106
+ return self.lngroup
107
+ else # unknown
108
+ return nil
109
+ end
110
+ end
111
+
112
+ # retorna un array con facturas de esta suscripcion
113
+ def invoices()
114
+ a = []
115
+ if self.subscr_id.to_s.size > 0
116
+ DB[
117
+ "SELECT DISTINCT i.id " +
118
+ "FROM invoice i WITH (NOLOCK) " +
119
+ "JOIN buffer_paypal_notification b WITH (NOLOCK) ON (b.id=i.id_buffer_paypal_notification AND b.subscr_id='#{self.subscr_id.to_s}') "
120
+ ].all { |row|
121
+ a << BlackStack::Invoice.where(:id=>row[:id]).first
122
+ # release resources
123
+ DB.disconnect
124
+ GC.start
125
+ }
126
+ end
127
+ a
128
+ end # def invoices
129
+
130
+ end # class PayPalSubscription
131
+ end # module BlackStack
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: invoicing_payments_processing
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Leandro Daniel Sardi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-01-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: websocket
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.2.8
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.2.8
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 1.2.8
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.2.8
33
+ - !ruby/object:Gem::Dependency
34
+ name: json
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 1.8.1
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 1.8.1
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: 1.8.1
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.8.1
53
+ - !ruby/object:Gem::Dependency
54
+ name: tiny_tds
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: 1.0.5
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 1.0.5
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 1.0.5
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.0.5
73
+ - !ruby/object:Gem::Dependency
74
+ name: sequel
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: 4.28.0
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 4.28.0
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 4.28.0
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 4.28.0
93
+ - !ruby/object:Gem::Dependency
94
+ name: pampa_workers
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: 1.1.1
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 1.1.1
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: 1.1.1
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 1.1.1
113
+ description: 'THIS GEM IS STILL IN DEVELOPMENT STAGE. Find documentation here: https://github.com/leandrosardi/invoicing_payments_processing.'
114
+ email: leandro.sardi@expandedventure.com
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - lib/balance.rb
120
+ - lib/bufferpaypalnotification.rb
121
+ - lib/customplan.rb
122
+ - lib/extend_client_by_invoicing_payments_processing.rb
123
+ - lib/invoice.rb
124
+ - lib/invoiceitem.rb
125
+ - lib/invoicing_payments_processing.rb
126
+ - lib/movement.rb
127
+ - lib/paypalsubscription.rb
128
+ homepage: https://rubygems.org/gems/invoicing_payments_processing
129
+ licenses:
130
+ - MIT
131
+ metadata: {}
132
+ post_install_message:
133
+ rdoc_options: []
134
+ require_paths:
135
+ - lib
136
+ required_ruby_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ requirements: []
147
+ rubyforge_project:
148
+ rubygems_version: 2.4.5.1
149
+ signing_key:
150
+ specification_version: 4
151
+ summary: THIS GEM IS STILL IN DEVELOPMENT STAGE. Invoicing and Payments Processing
152
+ gem (a.k.a. I+2P) is a Ruby gem to setup amazing offers in your website, track them,
153
+ and also process payments automatically using PayPal.
154
+ test_files: []