invoicing_payments_processing 1.1.1

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.
@@ -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: []