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.
- checksums.yaml +7 -0
- data/lib/balance.rb +27 -0
- data/lib/bufferpaypalnotification.rb +391 -0
- data/lib/customplan.rb +33 -0
- data/lib/extend_client_by_invoicing_payments_processing.rb +128 -0
- data/lib/invoice.rb +617 -0
- data/lib/invoiceitem.rb +11 -0
- data/lib/invoicing_payments_processing.rb +127 -0
- data/lib/movement.rb +138 -0
- data/lib/paypalsubscription.rb +131 -0
- metadata +154 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c2495e0d2257ff3cec7862dad5c29aa0ef6f374e
|
4
|
+
data.tar.gz: 684caeb185db9734c8c0977ebceed61b88d3465a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 89f4f7b34728ae6b95871ee58baf22dee4cf83827797cad575beab80ad46f4808f89be79b845006421e9c31ec51d29e388e8cb60efda0a4e403e67c606ca8559
|
7
|
+
data.tar.gz: b42b1696f7a099a713545ab2b2075dbf0f782336a3218a94f3c3312249e71c6cca4e7952eb6219737d809865be6740f723cef6c351d45be836b491a9637abcb8
|
data/lib/balance.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module BlackStack
|
2
|
+
class Balance
|
3
|
+
attr_accessor :client, :product_code, :amount, :credits
|
4
|
+
|
5
|
+
def initialize(id_client, product_code)
|
6
|
+
self.client = BlackStack::Client.where(:id => id_client).first
|
7
|
+
self.product_code = product_code
|
8
|
+
self.calculate()
|
9
|
+
end
|
10
|
+
|
11
|
+
def calculate()
|
12
|
+
q =
|
13
|
+
"select cast(sum(cast(amount as numeric(18,12))) as numeric(18,6)) as amount, sum(credits) as credits " +
|
14
|
+
"from movement with (nolock index(IX_movement__id_client__product_code)) " +
|
15
|
+
"where id_client='#{self.client.id}' " +
|
16
|
+
"and product_code='#{self.product_code}' "
|
17
|
+
row = DB[q].first
|
18
|
+
self.amount = row[:amount].to_f
|
19
|
+
self.credits = row[:credits].to_f
|
20
|
+
# libero recursos
|
21
|
+
DB.disconnect
|
22
|
+
GC.start
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end # module BlackStack
|
26
|
+
|
27
|
+
|
@@ -0,0 +1,391 @@
|
|
1
|
+
module BlackStack
|
2
|
+
class BufferPayPalNotification < Sequel::Model(:buffer_paypal_notification)
|
3
|
+
BlackStack::BufferPayPalNotification.dataset = BlackStack::BufferPayPalNotification.dataset.disable_insert_output
|
4
|
+
self.dataset = self.dataset.disable_insert_output
|
5
|
+
|
6
|
+
TXN_STATUS_CENCELED_REVERSAL = "Canceled_Reversal" # reembolso hecho por PayPal, luego de una disputa
|
7
|
+
TXN_STATUS_COMPLETED = "Completed"
|
8
|
+
TXN_STATUS_FAILED = "Failed"
|
9
|
+
TXN_STATUS_PENDING = "Pending"
|
10
|
+
TXN_STATUS_REFUNDED = "Refunded"
|
11
|
+
TXN_STATUS_REVERSED = "Reversed"
|
12
|
+
|
13
|
+
TXN_TYPE_SUBSCRIPTION_SIGNUP = "subscr_signup"
|
14
|
+
TXN_TYPE_SUBSCRIPTION_PAYMENT = "subscr_payment"
|
15
|
+
TXN_TYPE_SUBSCRIPTION_CANCEL = "subscr_cancel"
|
16
|
+
TXN_TYPE_SUBSCRIPTION_FAILED = "subscr_failed"
|
17
|
+
TXN_TYPE_SUBSCRIPTION_REFUND = ""
|
18
|
+
TXN_TYPE_SUBSCRIPTION_MODIFY = "subscr_modify"
|
19
|
+
TXN_TYPE_SUBSCRIPTION_SUSPEND = "recurring_payment_suspended"
|
20
|
+
TXN_TYPE_SUBSCRIPTION_SUSPEND_DUE_MAX_FAILURES = "recurring_payment_suspended_due_to_max_failed_payment"
|
21
|
+
TXN_TYPE_WEB_ACCEPT = "web_accept" # one time payment
|
22
|
+
# TXN_TYPE_SUBSCRIPTION_EOT = "subscr_eot" # no sabemos para que es esto
|
23
|
+
|
24
|
+
PAYMENT_STATUS_COMPLETED = "Completed"
|
25
|
+
|
26
|
+
LOCKING_FILENAME = "./accounting.bufferpaypalnotification.lock"
|
27
|
+
LOGGING_FILENAME = "./accounting.bufferpaypalnotification.log"
|
28
|
+
@@fd = File.open(LOCKING_FILENAME,"w")
|
29
|
+
|
30
|
+
def isSubscription?
|
31
|
+
self.txn_type =~ /^subscr_/
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.revenue()
|
35
|
+
DB["SELECT ISNULL(SUM(CAST(payment_gross AS NUMERIC(18,4))),0) AS revenue FROM buffer_paypal_notification WITH (NOLOCK) WHERE txn_type='#{BlackStack::BufferPayPalNotification::TXN_TYPE_SUBSCRIPTION_PAYMENT}'"].first[:revenue]
|
36
|
+
end
|
37
|
+
|
38
|
+
# busca al cliente relacionado con este pago, de tres formas:
|
39
|
+
# 1) haciendo coincidir el campo client.paypal_email con el BlackStack::BufferPayPalNotification.payer_email
|
40
|
+
# 2) haciendo coincidir el campo user.email con el BlackStack::BufferPayPalNotification.payer_email
|
41
|
+
# 3) haciendo coincidir el campo payer_email de alguna suscripcion existente con el BlackStack::BufferPayPalNotification.payer_email
|
42
|
+
# 3) haciendo coincidir el primer guid en el codigo de invoice, con el id del cliente
|
43
|
+
def get_client()
|
44
|
+
# obtengo el cliente que machea con este perfil
|
45
|
+
c = nil
|
46
|
+
if (c == nil)
|
47
|
+
cid = self.invoice.split(".").first.to_s
|
48
|
+
if cid.guid?
|
49
|
+
c = BlackStack::Client.where(:id=>cid).first
|
50
|
+
end
|
51
|
+
end
|
52
|
+
if (c == nil)
|
53
|
+
c = BlackStack::Client.where(:paypal_email=>self.payer_email).first
|
54
|
+
if (c == nil)
|
55
|
+
u = User.where(:email=>self.payer_email).first
|
56
|
+
if (u!=nil)
|
57
|
+
c = u.client
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
if (c == nil)
|
62
|
+
s = BlackStack::PayPalSubscription.where(:payer_email=>self.payer_email).first
|
63
|
+
if (s!=nil)
|
64
|
+
c = s.client
|
65
|
+
end
|
66
|
+
end
|
67
|
+
if (c == nil)
|
68
|
+
# obtengo el cliente - poco elegante
|
69
|
+
q =
|
70
|
+
"SELECT TOP 1 i.id_client AS cid " +
|
71
|
+
"FROM buffer_paypal_notification b WITH (NOLOCK) " +
|
72
|
+
"JOIN invoice i WITH (NOLOCK) ON b.id=i.id_buffer_paypal_notification " +
|
73
|
+
"WHERE b.id<>'#{self.id}' AND b.payer_id = '#{self.payer_id}' "
|
74
|
+
row = DB[q].first
|
75
|
+
if (row!=nil)
|
76
|
+
# obtengo el cliente al que corresponde este IPN
|
77
|
+
c = BlackStack::Client.where(:id=>row[:cid]).first
|
78
|
+
end
|
79
|
+
end
|
80
|
+
c
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.lock()
|
84
|
+
@@fd.flock(File::LOCK_EX)
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.release()
|
88
|
+
@@fd.flock(File::LOCK_UN)
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
# -----------------------------------------------------------------------------------------
|
93
|
+
# Factory
|
94
|
+
# -----------------------------------------------------------------------------------------
|
95
|
+
|
96
|
+
def self.load(params)
|
97
|
+
BlackStack::BufferPayPalNotification.where(
|
98
|
+
:verify_sign=>params['verify_sign'],
|
99
|
+
:txn_type=>params['txn_type'],
|
100
|
+
:ipn_track_id=>params['ipn_track_id']
|
101
|
+
).first
|
102
|
+
end
|
103
|
+
|
104
|
+
# crea un nuevo objeto BufferPayPalNotification, y le mapea los atributos en el hash params.
|
105
|
+
# no guarda el objeto en la base de datos.
|
106
|
+
# retorna el objeto creado.
|
107
|
+
def self.create(params)
|
108
|
+
b = BlackStack::BufferPayPalNotification.new()
|
109
|
+
b.txn_type = params['txn_type'].to_s
|
110
|
+
b.subscr_id = params['subscr_id'].to_s
|
111
|
+
b.last_name = params['last_name'].to_s
|
112
|
+
b.residence_country = params['residence_country'].to_s
|
113
|
+
b.mc_currency = params['mc_currency'].to_s
|
114
|
+
b.item_name = params['item_name'].to_s
|
115
|
+
b.amount1 = params['amount1'].to_s
|
116
|
+
b.business = params['business'].to_s
|
117
|
+
b.amount3 = params['amount3'].to_s
|
118
|
+
b.recurring = params['recurring'].to_s
|
119
|
+
b.verify_sign = params['verify_sign'].to_s
|
120
|
+
b.payer_status = params['payer_status'].to_s
|
121
|
+
b.test_ipn = params['test_ipn'].to_s
|
122
|
+
b.payer_email = params['payer_email'].to_s
|
123
|
+
b.first_name = params['first_name'].to_s
|
124
|
+
b.receiver_email = params['receiver_email'].to_s
|
125
|
+
b.payer_id = params['payer_id'].to_s
|
126
|
+
b.invoice = params['invoice'].to_s
|
127
|
+
b.reattempt = params['reattempt'].to_s
|
128
|
+
b.item_number = params['item_number'].to_s
|
129
|
+
b.subscr_date = params['subscr_date'].to_s
|
130
|
+
b.charset = params['charset'].to_s
|
131
|
+
b.notify_version = params['notify_version'].to_s
|
132
|
+
b.period1 = params['period1'].to_s
|
133
|
+
b.mc_amount1 = params['mc_amount1'].to_s
|
134
|
+
b.period3 = params['period3'].to_s
|
135
|
+
b.mc_amount3 = params['mc_amount3'].to_s
|
136
|
+
b.ipn_track_id = params['ipn_track_id'].to_s
|
137
|
+
b.transaction_subject = params['transaction_subject'].to_s
|
138
|
+
b.payment_date = params['payment_date'].to_s
|
139
|
+
b.payment_gross = params['payment_gross'].to_s
|
140
|
+
b.payment_type = params['payment_type'].to_s
|
141
|
+
b.txn_id = params['txn_id'].to_s
|
142
|
+
b.receiver_id = params['receiver_id'].to_s
|
143
|
+
b.payment_status = params['payment_status'].to_s
|
144
|
+
b.payment_fee = params['payment_fee'].to_s
|
145
|
+
b
|
146
|
+
end
|
147
|
+
|
148
|
+
# segun los atributos en el hash params, obtiene el objeto BufferPayPalNotification de a base de datos.
|
149
|
+
# si el objeto no existe, entonces crea un nuevo registro en la base de datos.
|
150
|
+
#
|
151
|
+
# este metodo es invocado desde el access point que recive las notificaciones de PayPal.
|
152
|
+
# por lo tanto, ejecuta mecanismos de bloqueo para manejar la concurrencia.
|
153
|
+
#
|
154
|
+
# retorna el objeto creado o cargado de la base de datos.
|
155
|
+
def self.parse(params)
|
156
|
+
begin
|
157
|
+
# Levantar el flag de reserva a mi favor
|
158
|
+
self.lock()
|
159
|
+
|
160
|
+
# escribo la notificacion cruda en un archivo de log, en caso que falle el mapeo a la base de datos por error del programador
|
161
|
+
File.open(LOGGING_FILENAME, 'a') { |file| file.puts(params.to_s) }
|
162
|
+
|
163
|
+
# si la notificacion no existe en la base de datos, la inserto
|
164
|
+
# si la notificacion ya existe entonces la actualizo, porque puede tratarse de un pago que no se habia podido completar (payment_status=='Completed')
|
165
|
+
b = BlackStack::BufferPayPalNotification.load(params)
|
166
|
+
if (b==nil)
|
167
|
+
b = self.create(params)
|
168
|
+
b.id = guid
|
169
|
+
b.create_time = now
|
170
|
+
b.save
|
171
|
+
end
|
172
|
+
|
173
|
+
# desbloquear
|
174
|
+
self.release()
|
175
|
+
|
176
|
+
return b
|
177
|
+
rescue => e
|
178
|
+
# ante cualquier falla, lo primero es desbloquear
|
179
|
+
self.release()
|
180
|
+
raise e
|
181
|
+
end
|
182
|
+
end # self.parse
|
183
|
+
|
184
|
+
|
185
|
+
def to_hash()
|
186
|
+
ret = {}
|
187
|
+
ret['id'] = self.id
|
188
|
+
ret['create_time'] = self.create_time.to_api
|
189
|
+
ret['txn_type'] = self.txn_type
|
190
|
+
ret['subscr_id'] = self.subscr_id
|
191
|
+
ret['last_name'] = self.last_name
|
192
|
+
ret['residence_country'] = self.residence_country
|
193
|
+
ret['mc_currency'] = self.mc_currency
|
194
|
+
ret['item_name'] = self.item_name
|
195
|
+
ret['amount1'] = self.amount1
|
196
|
+
ret['business'] = self.business
|
197
|
+
ret['amount3'] = self.amount3
|
198
|
+
ret['recurring'] = self.recurring
|
199
|
+
ret['verify_sign'] = self.verify_sign
|
200
|
+
ret['payer_status'] = self.payer_status
|
201
|
+
ret['test_ipn'] = self.test_ipn
|
202
|
+
ret['payer_email'] = self.payer_email
|
203
|
+
ret['first_name'] = self.first_name
|
204
|
+
ret['receiver_email'] = self.receiver_email
|
205
|
+
ret['payer_id'] = self.payer_id
|
206
|
+
ret['invoice'] = self.invoice
|
207
|
+
ret['reattempt'] = self.reattempt
|
208
|
+
ret['item_number'] = self.item_number
|
209
|
+
ret['subscr_date'] = self.subscr_date
|
210
|
+
ret['charset'] = self.charset
|
211
|
+
ret['notify_version'] = self.notify_version
|
212
|
+
ret['period1'] = self.period1
|
213
|
+
ret['mc_amount1'] = self.mc_amount1
|
214
|
+
ret['period3'] = self.period3
|
215
|
+
ret['mc_amount3'] = self.mc_amount3
|
216
|
+
ret['ipn_track_id'] = self.ipn_track_id
|
217
|
+
ret['transaction_subject'] = self.transaction_subject
|
218
|
+
ret['payment_date'] = self.payment_date
|
219
|
+
ret['payment_gross'] = self.payment_gross
|
220
|
+
ret['payment_type'] = self.payment_type
|
221
|
+
ret['txn_id'] = self.txn_id
|
222
|
+
ret['receiver_id'] = self.receiver_id
|
223
|
+
ret['payment_status'] = self.payment_status
|
224
|
+
ret['payment_fee'] = self.payment_fee
|
225
|
+
ret
|
226
|
+
end
|
227
|
+
|
228
|
+
# salda facturas
|
229
|
+
# genera nuevas facturas para pagos futuros
|
230
|
+
def self.process(params)
|
231
|
+
DB.transaction do
|
232
|
+
# verifico que no existe ya una notificacion
|
233
|
+
b = BlackStack::BufferPayPalNotification.where(:id=>params[:id]).first
|
234
|
+
if (b==nil)
|
235
|
+
# proceso la notificacion
|
236
|
+
b = BlackStack::BufferPayPalNotification.create(params)
|
237
|
+
# inserto la notificacion en la base de datos
|
238
|
+
b.id = params['id']
|
239
|
+
b.create_time = params['create_time'].api_to_sql_datetime
|
240
|
+
b.save
|
241
|
+
end
|
242
|
+
|
243
|
+
if !BlackStack::Invoice.where(:id_buffer_paypal_notification=>b.id).first.nil?
|
244
|
+
raise "IPN already linked to an invoice."
|
245
|
+
end
|
246
|
+
|
247
|
+
# varifico que el cliente exista
|
248
|
+
c = b.get_client
|
249
|
+
if (c == nil)
|
250
|
+
raise "Client not found (payer_email=#{b.payer_email.to_s})."
|
251
|
+
end
|
252
|
+
|
253
|
+
# parseo en nuemero de factura formado por id_client.id_invoice
|
254
|
+
cid = c.id.to_guid
|
255
|
+
iid = b.invoice.split(/\./).last # NOTA: el campo invoice tiene formato "cid.iid", pero originalmente solo tenia el formato "iid"
|
256
|
+
|
257
|
+
# si es un pago por un primer trial, sengundo trial o pago recurrente de suscripcion,
|
258
|
+
# entonces registro la factura, activo el rol de este cliente (deprecated), se agrega el cliente a la lista de emails (deprecated)
|
259
|
+
if ( ( b.txn_type == BlackStack::BufferPayPalNotification::TXN_TYPE_WEB_ACCEPT || b.txn_type == BlackStack::BufferPayPalNotification::TXN_TYPE_SUBSCRIPTION_PAYMENT || b.txn_type == BufferPayPalNotification::TXN_STATUS_COMPLETED ) && b.payment_status == 'Completed' )
|
260
|
+
# reviso si la factura ya estaba creada.
|
261
|
+
# la primer factura se por adelantado cuando el cliente hace signup, y antes de que se suscriba a paypal, y se le pone un ID igual a primer GUID del campo invoice del IPN
|
262
|
+
i = BlackStack::Invoice.where(:id=>iid, :status=>BlackStack::Invoice::STATUS_UNPAID).order(:billing_period_from).first
|
263
|
+
if (i == nil)
|
264
|
+
i = BlackStack::Invoice.where(:id=>iid, :status=>nil).order(:billing_period_from).first
|
265
|
+
end
|
266
|
+
|
267
|
+
# de la segunda factura en adelante, se generan con un ID diferente, pero se le guarda a subscr_id para identificar cuado llegue el pago de esa factura
|
268
|
+
if (i == nil)
|
269
|
+
# busco una factura en estado UNPAID que este vinculada a esta suscripcion
|
270
|
+
q =
|
271
|
+
"SELECT TOP 1 i.id AS iid " +
|
272
|
+
"FROM invoice i WITH (NOLOCK) " +
|
273
|
+
"WHERE i.id_client='#{cid}' " +
|
274
|
+
"AND ISNULL(i.status,#{BlackStack::Invoice::STATUS_UNPAID})=#{Invoice::STATUS_UNPAID} " +
|
275
|
+
"AND i.subscr_id = '#{b.subscr_id}' " +
|
276
|
+
"ORDER BY i.billing_period_from ASC "
|
277
|
+
row = DB[q].first
|
278
|
+
if row != nil
|
279
|
+
i = BlackStack::Invoice.where(:id=>row[:iid]).first
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
# valido haber encontrado la factura
|
284
|
+
raise "Invoice not found" if i.nil?
|
285
|
+
|
286
|
+
# valido que el importe de la factura sea igual al importe del IPN
|
287
|
+
raise "Invoice amount is not the equal to the amount of the IPN (#{i.total.to_s}!=#{b.payment_gross.to_s})" if i.total.to_f != b.payment_gross.to_f
|
288
|
+
|
289
|
+
# le asigno el id_buffer_paypal_notification
|
290
|
+
i.id_buffer_paypal_notification = b.id
|
291
|
+
i.save
|
292
|
+
|
293
|
+
# marco la factura como pagada
|
294
|
+
# registro contable - bookkeeping
|
295
|
+
i.getPaid() if i.canBePaid?
|
296
|
+
|
297
|
+
# crea una factura para el periodo siguiente (dia, semana, mes, anio)
|
298
|
+
j = BlackStack::Invoice.new()
|
299
|
+
j.id = guid()
|
300
|
+
j.id_client = c.id
|
301
|
+
j.create_time = now()
|
302
|
+
j.disabled_for_trial_ssm = c.disabled_for_trial_ssm
|
303
|
+
j.save()
|
304
|
+
|
305
|
+
# genero los datos de esta factura, como la siguiente factura a la que estoy pagando en este momento
|
306
|
+
j.next(i)
|
307
|
+
|
308
|
+
# creo el milestone con todo el credito pendiente que tiene esta subscripcion
|
309
|
+
buff_payment = i.buffer_paypal_notification
|
310
|
+
buff_signup = BlackStack::BufferPayPalNotification.where(:txn_type=>"subscr_signup", :subscr_id=>buff_payment.subscr_id).first
|
311
|
+
subs = buff_signup == nil ? nil : BlackStack::PayPalSubscription.where(:id_buffer_paypal_notification => buff_signup.id).first
|
312
|
+
|
313
|
+
elsif (b.txn_type == BlackStack::BufferPayPalNotification::TXN_TYPE_SUBSCRIPTION_SIGNUP)
|
314
|
+
# crear un registro en la tabla paypal_subscriptions
|
315
|
+
if BlackStack::PayPalSubscription.load(b.to_hash) != nil
|
316
|
+
# mensaje de error
|
317
|
+
raise 'Subscription Already Exists.'
|
318
|
+
else
|
319
|
+
# registro la suscripcion en la base de datos
|
320
|
+
s = BlackStack::PayPalSubscription.create(b.to_hash)
|
321
|
+
s.id = guid
|
322
|
+
s.id_buffer_paypal_notification = b.id
|
323
|
+
s.create_time = now
|
324
|
+
s.id_client = c.id
|
325
|
+
s.active = true
|
326
|
+
s.save
|
327
|
+
end
|
328
|
+
|
329
|
+
elsif (
|
330
|
+
b.txn_type == BlackStack::BufferPayPalNotification::TXN_TYPE_SUBSCRIPTION_CANCEL #||
|
331
|
+
#b.txn_type == BlackStack::BufferPayPalNotification::TXN_TYPE_SUBSCRIPTION_SUSPEND || # estos IPN traen el campo subscr_id en blanco
|
332
|
+
#b.txn_type == BlackStack::BufferPayPalNotification::TXN_TYPE_SUBSCRIPTION_SUSPEND_DUE_MAX_FAILURES # estos IPN traen el campo subscr_id en blanco
|
333
|
+
)
|
334
|
+
s = BlackStack::PayPalSubscription.load(b)
|
335
|
+
if s == nil
|
336
|
+
# mensaje de error
|
337
|
+
raise 'Subscription Not Found.'
|
338
|
+
else
|
339
|
+
# registro la suscripcion en la base de datos
|
340
|
+
s.active = false
|
341
|
+
s.save
|
342
|
+
end
|
343
|
+
|
344
|
+
elsif (b.txn_type == BlackStack::BufferPayPalNotification::TXN_TYPE_SUBSCRIPTION_FAILED)
|
345
|
+
# TODO: actualizar registro en la tabla paypal_subscriptions. notificar al usuario
|
346
|
+
|
347
|
+
elsif (b.txn_type == BlackStack::BufferPayPalNotification::TXN_TYPE_SUBSCRIPTION_MODIFY)
|
348
|
+
# TODO: ?
|
349
|
+
|
350
|
+
elsif (b.txn_type.to_s == BlackStack::BufferPayPalNotification::TXN_TYPE_SUBSCRIPTION_REFUND)
|
351
|
+
# PROBLEMA: Los IPN no dicen de a qué pago corresponde el reembolso
|
352
|
+
|
353
|
+
payment_gross = 0
|
354
|
+
if b.payment_status == BlackStack::BufferPayPalNotification::TXN_STATUS_CENCELED_REVERSAL
|
355
|
+
payment_gross = 0 - b.payment_gross.to_f - b.payment_fee.to_f
|
356
|
+
elsif b.payment_status == BlackStack::BufferPayPalNotification::TXN_STATUS_REFUNDED || b.payment_status == BlackStack::BufferPayPalNotification::TXN_STATUS_REVERSED
|
357
|
+
payment_gross = b.payment_gross.to_f # en negativo
|
358
|
+
end
|
359
|
+
if payment_gross < 0
|
360
|
+
# verifico que la factura por este IPN no exista
|
361
|
+
j = BlackStack::Invoice.where(:id_buffer_paypal_notification=>b.id).first
|
362
|
+
if (j!=nil)
|
363
|
+
raise 'Invoice already exists.'
|
364
|
+
end
|
365
|
+
|
366
|
+
# creo la factura por el reembolso
|
367
|
+
i = BlackStack::Invoice.new()
|
368
|
+
i.id = guid()
|
369
|
+
i.id_client = c.id
|
370
|
+
i.create_time = now()
|
371
|
+
i.disabled_for_trial_ssm = c.disabled_for_trial_ssm
|
372
|
+
i.id_buffer_paypal_notification = b.id
|
373
|
+
i.status = BlackStack::Invoice::STATUS_REFUNDED
|
374
|
+
i.billing_period_from = b.create_time
|
375
|
+
i.billing_period_to = b.create_time
|
376
|
+
i.paypal_url = nil
|
377
|
+
i.disabled_for_add_remove_items = true
|
378
|
+
i.save()
|
379
|
+
|
380
|
+
# parseo el reeembolso - creo el registro contable
|
381
|
+
i.setup_refund(payment_gross, iid)
|
382
|
+
|
383
|
+
end
|
384
|
+
else
|
385
|
+
# unknown
|
386
|
+
|
387
|
+
end
|
388
|
+
end # DB.transaction
|
389
|
+
end # def process
|
390
|
+
end # class
|
391
|
+
end # module BlackStack
|
data/lib/customplan.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module BlackStack
|
2
|
+
class CustomPlan < Sequel::Model(:custom_plan)
|
3
|
+
BlackStack::CustomPlan.dataset = BlackStack::CustomPlan.dataset.disable_insert_output
|
4
|
+
self.dataset = self.dataset.disable_insert_output
|
5
|
+
|
6
|
+
many_to_one :client, :class=>:'BlackStack::Client', :key=>:id_client
|
7
|
+
|
8
|
+
def to_hash
|
9
|
+
h = {}
|
10
|
+
h[:type] = self.type # not null
|
11
|
+
h[:item_number] = self.item_number # not null
|
12
|
+
h[:name] = self.name # not null
|
13
|
+
h[:credits] = self.credits # not null
|
14
|
+
h[:fee] = self.fee # not null
|
15
|
+
h[:period] = self.period # not null
|
16
|
+
h[:units] = self.units # not null
|
17
|
+
|
18
|
+
h[:trial_credits] = self.trial_credits if self.trial_credits != nil
|
19
|
+
h[:trial_fee] = self.trial_fee if self.trial_fee != nil
|
20
|
+
h[:trial_period] = self.trial_period if self.trial_period != nil
|
21
|
+
h[:trial_units] = self.trial_units if self.trial_units != nil
|
22
|
+
|
23
|
+
h[:trial2_credits] = self.trial2_credits if self.trial2_credits != nil
|
24
|
+
h[:trial2_fee] = self.trial2_fee if self.trial2_fee != nil
|
25
|
+
h[:trial2_period] = self.trial2_period if self.trial2_period != nil
|
26
|
+
h[:trial2_units] = self.trial2_units if self.trial2_units != nil
|
27
|
+
|
28
|
+
h[:description] = self.description # not null
|
29
|
+
h
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end # module BlackStack
|