invoicing_payments_processing 1.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/balance.rb +27 -0
- data/lib/bufferpaypalnotification.rb +404 -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: 05607ccf6270087041f4204da5d1175f4cbb69ad
|
4
|
+
data.tar.gz: 178616b62e1baf3a71a768f03050219f28dcc019
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 59d53d7c5420dd4b7faefa1cc9e97a53672d46fd9520e9403026487577bab9dff54bd1341a2f5910e1d9f0096d87e197945bb9b7649e6516c5e71643055d8f99
|
7
|
+
data.tar.gz: 394faf0fa9d17fd5a317505077923f25eb426e4c13be69334fc65b52568faa2ca32a77ab4b5c184c3b76423c1eeba67acec75950e2554856ccc0282a5768ae8f
|
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,404 @@
|
|
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.to_hash)
|
335
|
+
if s.nil?
|
336
|
+
# mensaje de error
|
337
|
+
raise "Subscription (#{b.subscr_id}) 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
|
+
# obtengo la ultima factura pagada, vinculada a un IPN con el mismo codigo invoice
|
367
|
+
row = DB[
|
368
|
+
"SELECT TOP 1 i.id " +
|
369
|
+
"FROM buffer_paypal_notification b " +
|
370
|
+
"JOIN invoice i ON ( b.id=i.id_buffer_paypal_notification AND i.status=#{BlackStack::Invoice::STATUS_PAID.to_s} ) " +
|
371
|
+
"WHERE b.invoice='#{b.invoice}' " +
|
372
|
+
"ORDER BY i.create_time DESC "
|
373
|
+
].first
|
374
|
+
if row.nil?
|
375
|
+
raise 'Previous Paid Invoice not found.'
|
376
|
+
end
|
377
|
+
k = BlackStack::Invoice.where(:id=>row[:id]).first
|
378
|
+
|
379
|
+
# creo la factura por el reembolso
|
380
|
+
i = BlackStack::Invoice.new()
|
381
|
+
i.id = guid()
|
382
|
+
i.id_client = c.id
|
383
|
+
i.create_time = now()
|
384
|
+
i.disabled_for_trial_ssm = c.disabled_for_trial_ssm
|
385
|
+
i.id_buffer_paypal_notification = b.id
|
386
|
+
i.status = BlackStack::Invoice::STATUS_REFUNDED
|
387
|
+
i.billing_period_from = b.create_time
|
388
|
+
i.billing_period_to = b.create_time
|
389
|
+
i.paypal_url = nil
|
390
|
+
i.disabled_for_add_remove_items = true
|
391
|
+
i.save()
|
392
|
+
|
393
|
+
# parseo el reeembolso - creo el registro contable
|
394
|
+
i.setup_refund(payment_gross, k.id)
|
395
|
+
|
396
|
+
end
|
397
|
+
else
|
398
|
+
# unknown
|
399
|
+
|
400
|
+
end
|
401
|
+
end # DB.transaction
|
402
|
+
end # def process
|
403
|
+
end # class
|
404
|
+
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
|