invoicing_payments_processing 1.1.4 → 1.1.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 34bca7e013f48b4303f62310d10af93adb0150d5
4
- data.tar.gz: d5be57777768cf864a273d34c87350fc7b48e124
3
+ metadata.gz: 079660aadfbd7106fcc74787e3d86002375ec4b2
4
+ data.tar.gz: 281a650e189be44ed7e91ca2bf439e876f014eb8
5
5
  SHA512:
6
- metadata.gz: dc65a5b539e09a60dbdcc71ebf4cc1ff4c718f8235bbd6c7fe989b34fbd3daf4e32bd5ef9b053cf842e4e75855295d8185c5da1b8e86052c0a18a7fd915d84fd
7
- data.tar.gz: d71ff8365ca5a3142faee45b8da1de7a11b6c0a42b1169e7c493b9b38cd21e6b167676dc99aa2a499670aa03bfa615fd1c9b98af0b583218076ff9c720ec04d2
6
+ metadata.gz: 025fd8f5d7060f21b8bdd2e2d1d087ee57d3ac587c32b8c0aaf03f53fba263da0904b21a00d85d5c6d6fe9c220155783e9541bf2ba5dbf8c49aff3c0e58bea82
7
+ data.tar.gz: 875037f5c24e6702cfcb8c39bca913d22761e2f1a7ca3d8efe6707edc30a8a8ec0c12e24fdc22da2fb4fa7d1517f5c0463d85aed2744429de5ff77ffa5ecda14
@@ -43,13 +43,19 @@ module BlackStack
43
43
  def get_client()
44
44
  # obtengo el cliente que machea con este perfil
45
45
  c = nil
46
- if (c == nil)
46
+ if c.nil?
47
+ if self.invoice.guid?
48
+ i = BlackStack::Invoice.where(:id=>self.invoice).first
49
+ c = i.client if !i.nil?
50
+ end
51
+ end
52
+ if c.nil?
47
53
  cid = self.invoice.split(".").first.to_s
48
54
  if cid.guid?
49
55
  c = BlackStack::Client.where(:id=>cid).first
50
56
  end
51
57
  end
52
- if (c == nil)
58
+ if c.nil?
53
59
  c = BlackStack::Client.where(:paypal_email=>self.payer_email).first
54
60
  if (c == nil)
55
61
  u = User.where(:email=>self.payer_email).first
@@ -58,13 +64,13 @@ module BlackStack
58
64
  end
59
65
  end
60
66
  end
61
- if (c == nil)
67
+ if c.nil?
62
68
  s = BlackStack::PayPalSubscription.where(:payer_email=>self.payer_email).first
63
69
  if (s!=nil)
64
70
  c = s.client
65
71
  end
66
72
  end
67
- if (c == nil)
73
+ if c.nil?
68
74
  # obtengo el cliente - poco elegante
69
75
  q =
70
76
  "SELECT TOP 1 i.id_client AS cid " +
@@ -288,6 +294,7 @@ module BlackStack
288
294
 
289
295
  # le asigno el id_buffer_paypal_notification
290
296
  i.id_buffer_paypal_notification = b.id
297
+ i.subscr_id = b.subscr_id
291
298
  i.save
292
299
 
293
300
  # marco la factura como pagada
@@ -363,6 +370,19 @@ module BlackStack
363
370
  raise 'Invoice already exists.'
364
371
  end
365
372
 
373
+ # obtengo la ultima factura pagada, vinculada a un IPN con el mismo codigo invoice
374
+ row = DB[
375
+ "SELECT TOP 1 i.id " +
376
+ "FROM buffer_paypal_notification b " +
377
+ "JOIN invoice i ON ( b.id=i.id_buffer_paypal_notification AND i.status=#{BlackStack::Invoice::STATUS_PAID.to_s} ) " +
378
+ "WHERE b.invoice='#{b.invoice}' " +
379
+ "ORDER BY i.create_time DESC "
380
+ ].first
381
+ if row.nil?
382
+ raise 'Previous Paid Invoice not found.'
383
+ end
384
+ k = BlackStack::Invoice.where(:id=>row[:id]).first
385
+
366
386
  # creo la factura por el reembolso
367
387
  i = BlackStack::Invoice.new()
368
388
  i.id = guid()
@@ -378,7 +398,7 @@ module BlackStack
378
398
  i.save()
379
399
 
380
400
  # parseo el reeembolso - creo el registro contable
381
- i.setup_refund(payment_gross, iid)
401
+ i.setup_refund(payment_gross, k.id)
382
402
 
383
403
  end
384
404
  else
@@ -10,9 +10,112 @@ module BlackStack
10
10
 
11
11
  # crea/actualiza un registro en la tabla movment, reduciendo la cantidad de creditos y saldo que tiene el cliente, para el producto indicado en product_code.
12
12
  def consume(product_code, number_of_credits=1, description=nil)
13
- DB.execute("exec reduceDebt '#{product_code.to_sql}', '#{self.id}', #{number_of_credits.to_s}, '#{description.to_s.to_sql}'")
13
+ # create the consumtion
14
+ total_credits = 0.to_f - BlackStack::Balance.new(self.id, product_code).credits.to_f
15
+ total_amount = 0.to_f - BlackStack::Balance.new(self.id, product_code).amount.to_f
16
+ ratio = total_credits == 0 ? 0.to_f : total_amount.to_f / total_credits.to_f
17
+ amount = number_of_credits.to_f * ratio
18
+ cons = BlackStack::Movement.new
19
+ cons.id = guid()
20
+ cons.id_client = self.id
21
+ cons.create_time = now()
22
+ cons.type = BlackStack::Movement::MOVEMENT_TYPE_CANCELATION
23
+ cons.description = description.nil? ? 'Consumption' : description
24
+ cons.paypal1_amount = 0
25
+ cons.bonus_amount = 0
26
+ cons.amount = amount
27
+ cons.credits = number_of_credits
28
+ cons.profits_amount = -amount
29
+ cons.product_code = product_code
30
+ cons.expiration_time = nil
31
+ cons.save
32
+ # if there is negative credits
33
+ prod = BlackStack::InvoicingPaymentsProcessing.product_descriptor(product_code)
34
+ total_credits = 0.to_f - BlackStack::Balance.new(self.id, product_code).credits.to_f
35
+ total_amount = 0.to_f - BlackStack::Balance.new(self.id, product_code).amount.to_f
36
+ sleep(2) # delay to ensure the time of the bonus movement will be later than the time of the consumption movement
37
+ if total_credits < 0
38
+ self.adjustment(product_code, total_amount, total_credits, 'Adjustment Because Quota Has Been Exceeded.')
39
+ end
40
+ # recaculate amounts in both consumptions and expirations - CANCELADO - Se debe hacer offline
41
+ #self.recalculate(product_code)
42
+ # return
43
+ cons
14
44
  end
15
-
45
+
46
+ # crea un registro en la tabla movment, reduciendo la cantidad de creditos con saldo importe 0, para el producto indicado en product_code.
47
+ def bonus(product_code, expiration, number_of_credits=1, description=nil)
48
+ bonus_amount = 0 # Los bonos siempre son por un importa igual a 0.
49
+
50
+ bonus = BlackStack::Movement.new
51
+ bonus.id = guid()
52
+ bonus.id_client = self.id
53
+ bonus.create_time = now()
54
+ bonus.type = BlackStack::Movement::MOVEMENT_TYPE_ADD_BONUS
55
+ bonus.description = description.nil? ? 'Bonus' : description
56
+ bonus.paypal1_amount = 0
57
+ bonus.bonus_amount = bonus_amount
58
+ bonus.amount = -bonus_amount
59
+ bonus.credits = -number_of_credits
60
+ bonus.profits_amount = 0
61
+ bonus.product_code = product_code
62
+ bonus.expiration_time = expiration
63
+ bonus.save
64
+ # recalculate - CANCELADO
65
+ #bonus.recalculate
66
+ # return
67
+ bonus
68
+ end
69
+
70
+ # crea un registro en la tabla movment, reduciendo la cantidad de creditos con saldo importe 0, para el producto indicado en product_code.
71
+ def adjustment(product_code, adjustment_amount=0, adjustment_credits=0, description=nil, type=BlackStack::Movement::MOVEMENT_TYPE_ADJUSTMENT)
72
+ adjust = BlackStack::Movement.new
73
+ adjust.id = guid()
74
+ adjust.id_client = self.id
75
+ adjust.create_time = now()
76
+ adjust.type = type
77
+ adjust.description = description.nil? ? 'Adjustment' : description
78
+ adjust.paypal1_amount = 0
79
+ adjust.bonus_amount = 0
80
+ adjust.amount = adjustment_amount
81
+ adjust.credits = adjustment_credits
82
+ adjust.profits_amount = -adjustment_amount
83
+ adjust.product_code = product_code
84
+ adjust.expiration_time = nil
85
+ adjust.save
86
+ adjust
87
+ end
88
+
89
+ # recalculate the amount for all the consumptions, expirations, and adjustments
90
+ def recalculate(product_code)
91
+ #
92
+ amount_paid = 0.to_f
93
+ credits_paid = 0
94
+
95
+ #total_credits = 0.to_f - BlackStack::Balance.new(self.id, product_code).credits.to_f
96
+ #total_amount = 0.to_f - BlackStack::Balance.new(self.id, product_code).amount.to_f
97
+
98
+ self.movements.select { |o|
99
+ o.product_code.upcase == product_code.upcase
100
+ }.sort_by { |o| o.create_time }.each { |o|
101
+ #if o.credits.to_f < 0 # payment or bonus
102
+ # if o.credits.to_f > 0 && ( o.type==BlackStack::Movement::MOVEMENT_TYPE_CANCELATION || o.type==BlackStack::Movement::MOVEMENT_TYPE_EXPIRATION ) # consumption or expiration
103
+ # consumption or expiration or bonus
104
+ if (
105
+ o.type==BlackStack::Movement::MOVEMENT_TYPE_CANCELATION ||
106
+ o.type==BlackStack::Movement::MOVEMENT_TYPE_EXPIRATION ||
107
+ o.type==BlackStack::Movement::MOVEMENT_TYPE_ADJUSTMENT
108
+ )
109
+ x = credits_paid.to_f == 0 ? 0 : o.credits.to_f * ( amount_paid.to_f / credits_paid.to_f )
110
+ o.amount = x
111
+ o.profits_amount = -x
112
+ o.save
113
+ end
114
+ amount_paid += 0.to_f - o.amount.to_f
115
+ credits_paid += 0.to_i - o.credits.to_i
116
+ }
117
+ end
118
+
16
119
  # TODO: el cliente deberia tener una FK a la tabla division. La relacion no puede ser N-N.
17
120
  # TODO: se debe preguntar a la central
18
121
  def division
@@ -11,6 +11,7 @@ module BlackStack
11
11
  many_to_one :buffer_paypal_notification, :class=>:'BlackStack::BufferPayPalNotification', :key=>:id_buffer_paypal_notification
12
12
  many_to_one :client, :class=>:'BlackStack::Client', :key=>:id_client
13
13
  many_to_one :paypal_subscription, :class=>:'BlackStack::PayPalSubscription', :key=>:id_paypal_subscription
14
+ many_to_one :previous, :class=>:'BlackStack::Invoice', :key=>:id_previous_invoice
14
15
  one_to_many :items, :class=>:'BlackStack::InvoiceItem', :key=>:id_invoice
15
16
 
16
17
  # compara 2 planes, y retorna TRUE si ambos pueden coexistir en una misma facutra, con un mismo enlace de PayPal
@@ -269,24 +270,67 @@ module BlackStack
269
270
  self.save()
270
271
  end
271
272
 
272
- # cambia el estado de la factura de UNPAID a PAID
273
- # verifica que el estado de la factura sea NULL o UNPAID
274
- # crea los registros contables por el pago de esta factura
275
- def getPaid()
273
+ # Verifica que el estado de la factura sea NULL o UNPAID.
274
+ # Cambia el estado de la factura de UNPAID a PAID.
275
+ # Crea los registros contables por el pago de esta factura: un registro por cada item, y por cada bono del plan en cada item.
276
+ # Los registros en la table de movimientos se registraran con la fecha del parametro sql_payment_datetime.
277
+ # Las fechas de expiracion de los movimientos se calculan seguin la fecha del pago.
278
+ #
279
+ # sql_payment_datetime: Fecha-hora del pago. Por defecto es la fecha-hora actual.
280
+ #
281
+ def getPaid(payment_time=nil)
282
+ payment_time = Time.now() if payment_time.nil?
283
+
276
284
  if self.canBePaid? == false
277
285
  raise "Method BlackStack::Invoice::getPaid requires the current status is nil or unpaid."
278
286
  end
279
287
  # marco la factura como pagada
280
288
  self.status = BlackStack::Invoice::STATUS_PAID
281
289
  self.save
290
+ # expiracion de creditos de la factura anterior
291
+ i = self.previous
292
+ if !i.nil?
293
+ InvoiceItem.where(:id_invoice=>i.id).all { |item|
294
+ #
295
+ BlackStack::Movement.where(:id_invoice_item => item.id).all { |mov|
296
+ #
297
+ mov.expire if mov.expiration_on_next_payment == true
298
+ #
299
+ DB.disconnect
300
+ GC.start
301
+ } # BlackStack::Movement.where(:id_invoice_item => item.id).all { |mov|
302
+ #
303
+ DB.disconnect
304
+ GC.start
305
+ } # InvoiceItem.where(:id_invoice=>i.id).all { |item|
306
+ end
282
307
  # registro los asientos contables
283
- InvoiceItem.where(:id_invoice=>self.id).all { |item|
284
- BlackStack::Movement.new().parse(item, BlackStack::Movement::MOVEMENT_TYPE_ADD_PAYMENT, "Invoice Payment").save()
308
+ InvoiceItem.where(:id_invoice=>self.id).all { |item|
309
+ # obtengo descriptor del plan
310
+ plan = BlackStack::InvoicingPaymentsProcessing.plan_descriptor(item.item_number)
311
+ # obtengo descriptor del producto
312
+ prod = BlackStack::InvoicingPaymentsProcessing.product_descriptor(plan[:product_code])
313
+ # registro el pago
314
+ BlackStack::Movement.new().parse(item, BlackStack::Movement::MOVEMENT_TYPE_ADD_PAYMENT, "Invoice Payment", payment_time, item.id).save()
315
+ # agrego los bonos de este plan
316
+ plan[:bonus_plans].each { |h|
317
+ plan_bonus = BlackStack::InvoicingPaymentsProcessing.plan_descriptor(h[:item_number])
318
+ raise "bonus plan not found" if plan_bonus.nil?
319
+ bonus = BlackStack::InvoiceItem.new
320
+ bonus.id = guid()
321
+ bonus.id_invoice = self.id
322
+ bonus.product_code = plan_bonus[:product_code]
323
+ bonus.unit_price = 0
324
+ bonus.units = plan_bonus[:credits]
325
+ bonus.amount = 0
326
+ bonus.item_number = plan_bonus[:item_number]
327
+ BlackStack::Movement.new().parse(bonus, BlackStack::Movement::MOVEMENT_TYPE_ADD_BONUS, 'Payment Bonus', payment_time, item.id).save()
328
+ }
285
329
  #
286
330
  DB.disconnect
287
331
  GC.start
288
332
  }
289
- end
333
+ end # def getPaid
290
334
 
291
335
  # Verify if I can add this item_number to this invoice.
292
336
  # Otherwise, it raise an exception.
@@ -450,10 +494,10 @@ module BlackStack
450
494
  # en este caso la factura se genera antes del pago.
451
495
  # crea uno o mas registros en la tabla invoice_item.
452
496
  def next(i)
453
- b = i.buffer_paypal_notification
454
- if b == nil
455
- raise "Method BlackStack::Invoice::next requires the previous invoice (i) is linked to a record in the table buffer_paypal_notification."
456
- end
497
+ # b = i.buffer_paypal_notification
498
+ # if b == nil
499
+ # raise "Method BlackStack::Invoice::next requires the previous invoice (i) is linked to a record in the table buffer_paypal_notification."
500
+ # end
457
501
 
458
502
  id_client = i.id_client
459
503
  c = BlackStack::Client.where(:id=>id_client).first
@@ -474,7 +518,8 @@ module BlackStack
474
518
  self.create_time = now()
475
519
  self.id_client = c.id
476
520
  self.id_buffer_paypal_notification = nil
477
- self.subscr_id = b.subscr_id
521
+ self.id_previous_invoice = i.id
522
+ self.subscr_id = i.subscr_id
478
523
  self.disabled_for_add_remove_items = true
479
524
 
480
525
  i.items.each { |t|
@@ -527,17 +572,14 @@ module BlackStack
527
572
  # cargo la factura
528
573
  i = BlackStack::Invoice.where(:id=>id_invoice).first
529
574
  raise "Invoice not found (#{id_invoice})" if i.nil?
530
-
531
575
  # obtengo el total de la factura
532
576
  total = i.total.to_f
533
-
534
577
  # Si existe un item, y solo uno, con importe igual al reembolso, entonces se aplica todo el reembolso a ese item. Y termina la funcion.
535
578
  # Si existen mas de un item con igual importe que el reembolso, entonces se levanta una excepcion.
536
579
  matched_items = i.items.select { |o| o.amount.to_f == -payment_gross.to_f }
537
-
580
+ #
538
581
  if total < -payment_gross
539
582
  raise "The refund is higher than the invoice amount (invoice #{id_invoice}, #{total.to_s}, #{payment_gross.to_s})"
540
-
541
583
  # Si el monto del reembolso es igual al total de la factura, se hace un reembolso total de todos los items. Y termina la funcion.
542
584
  # Si el monto de la factura es distinto al moneto del reembolso, entonces se levanta una excepcion.
543
585
  elsif total == -payment_gross
@@ -555,39 +597,28 @@ module BlackStack
555
597
  item1.detail = u.detail.to_s
556
598
  item1.description = u.description.to_s
557
599
  item1.save()
558
- BlackStack::Movement.new().parse(item1, BlackStack::Movement::MOVEMENT_TYPE_REFUND_BALANCE).save()
600
+ BlackStack::Movement.new().parse(item1, BlackStack::Movement::MOVEMENT_TYPE_REFUND_BALANCE, 'Full Refund').save()
601
+ # si el balance quedo en negativo, entonces aplico otro ajuste
602
+ net_amount = 0.to_f - BlackStack::Balance.new(self.client.id, u.product_code.to_s).amount.to_f
603
+ net_credits = 0.to_f - BlackStack::Balance.new(self.client.id, u.product_code.to_s).credits.to_f
604
+ if net_amount < 0 && net_credits < 0
605
+ adjust = self.client.adjustment(u.product_code.to_s, net_amount, net_credits, 'Adjustment for Negative Balance after Refund')
606
+ adjust.id_invoice_item = item1.id
607
+ adjust.save
608
+ end # if net_amount < 0
559
609
  # release resources
560
610
  DB.disconnect
561
611
  GC.start
562
- }
563
- =begin
564
- # si existe al menos un item que coincida el total con el monto del reembolso,
565
- # entonces selecciono el primero
566
- elsif matched_items.size > 0
567
- t = matched_items.first
568
- h = BlackStack::InvoicingPaymentsProcessing::plans_descriptor.select { |obj| obj[:item_number] == t.item_number }.first
569
- raise "Plan not found" if h.nil?
570
- item1 = BlackStack::InvoiceItem.new()
571
- item1.id = guid()
572
- item1.id_invoice = self.id
573
- item1.unit_price = t.unit_price.to_f
574
- item1.units = -t.units
575
- item1.amount = -t.amount.to_f
576
- item1.product_code = t.product_code.to_s
577
- item1.item_number = t.item_number.to_s
578
- item1.detail = t.detail.to_s
579
- item1.description = t.description.to_s
580
- item1.save()
581
- BlackStack::Movement.new().parse(item1, BlackStack::Movement::MOVEMENT_TYPE_REFUND_BALANCE).save()
582
- =end
612
+ } # i.items.each { |u|
583
613
  # reembolso parcial de una factura con un unico item
584
614
  elsif i.items.size == 1
585
615
  t = i.items.first
586
-
616
+ #
587
617
  amount = -payment_gross.to_f
588
618
  unit_price = t.amount.to_f / t.units.to_f
589
- units = (amount / unit_price.to_f).round.to_i
590
-
619
+ float_units = (amount / unit_price.to_f)
620
+ units = float_units.round.to_i
621
+ #
591
622
  h = BlackStack::InvoicingPaymentsProcessing::plans_descriptor.select { |obj| obj[:item_number] == t.item_number }.first
592
623
  raise "Plan not found" if h.nil?
593
624
  item1 = BlackStack::InvoiceItem.new()
@@ -601,13 +632,27 @@ module BlackStack
601
632
  item1.detail = t.detail.to_s
602
633
  item1.description = t.description.to_s
603
634
  item1.save()
604
- BlackStack::Movement.new().parse(item1, BlackStack::Movement::MOVEMENT_TYPE_REFUND_BALANCE).save()
605
-
635
+ BlackStack::Movement.new().parse(item1, BlackStack::Movement::MOVEMENT_TYPE_REFUND_BALANCE, 'Partial Refund').save()
636
+ # agrego un ajuste por el redondeo a una cantidad entera de creditos
637
+ if float_units.to_f != units.to_f
638
+ adjustment_amount = unit_price.to_f * (units.to_f - float_units.to_f)
639
+ adjust = self.client.adjustment(t.product_code.to_s, adjustment_amount, 0, 'Adjustment for Partial Refund', BlackStack::Movement::MOVEMENT_TYPE_REFUND_ADJUSTMENT)
640
+ adjust.id_invoice_item = item1.id
641
+ adjust.save
642
+ end
643
+ # si el balance quedo en negativo, entonces aplico otro ajuste
644
+ net_amount = 0.to_f - BlackStack::Balance.new(self.client.id, t.product_code.to_s).amount.to_f
645
+ net_credits = 0.to_f - BlackStack::Balance.new(self.client.id, t.product_code.to_s).credits.to_f
646
+ if net_amount < 0 && net_credits < 0
647
+ adjust = self.client.adjustment(t.product_code.to_s, net_amount, net_credits, 'Adjustment for Negative Balance')
648
+ adjust.id_invoice_item = item1.id
649
+ adjust.save
650
+ end # if net_amount < 0
651
+ # recalculo todos los consumos y expiraciones - CANCELADO - Debe hacerse offline
652
+ # self.client.recalculate(t.product_code.to_s)
606
653
  else
607
654
  raise "Refund amount is not matching with the invoice total (#{total.to_s}) and the invoice has more than 1 item."
608
-
609
655
  end
610
-
611
656
  # release resources
612
657
  DB.disconnect
613
658
  GC.start
@@ -10,8 +10,10 @@ module BlackStack
10
10
  MOVEMENT_TYPE_ADD_BONUS = 1
11
11
  MOVEMENT_TYPE_REASSIGN_BALANCE = 2
12
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
13
+ MOVEMENT_TYPE_CANCELATION = 4 # liability with the client is reduced due service delivery. it can be recalculated
14
+ MOVEMENT_TYPE_EXPIRATION = 5 # liability with the client is reduced due credits expiration. it can be recalculated
15
+ MOVEMENT_TYPE_ADJUSTMENT = 6 # it can be recalculated
16
+ MOVEMENT_TYPE_REFUND_ADJUSTMENT = 7 # it cannot be recalculated
15
17
 
16
18
  def typeName()
17
19
  if (self.type==MOVEMENT_TYPE_ADD_PAYMENT)
@@ -46,17 +48,20 @@ module BlackStack
46
48
  end
47
49
 
48
50
  # 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)
51
+ # type may be either MOVEMENT_TYPE_ADD_PAYMENT or MOVEMENT_TYPE_ADD_BONUS or MOVEMENT_TYPE_REFUND_BALANCE, but not other value
52
+ def parse(item, type=MOVEMENT_TYPE_ADD_PAYMENT, description='n/a', payment_time=nil, id_item=nil)
53
+ # the movment must be a payment or a bonus or a refund
54
+ raise 'Movement must be either a payment or bonus or refund' if type != MOVEMENT_TYPE_ADD_PAYMENT && type != MOVEMENT_TYPE_ADD_BONUS && type != MOVEMENT_TYPE_REFUND_BALANCE
55
+ #
56
+ payment_time = Time.now() if payment_time.nil?
57
+ plan = BlackStack::InvoicingPaymentsProcessing.plan_descriptor(item.item_number)
52
58
  prod = BlackStack::InvoicingPaymentsProcessing.product_descriptor(plan[:product_code])
53
-
54
59
  if (self.id==nil)
55
60
  self.id=guid()
56
61
  end
57
62
  self.id_client = item.invoice.id_client
58
- self.id_invoice_item = item.id
59
- self.create_time = item.invoice.billing_period_from
63
+ self.id_invoice_item = id_item.nil? ? item.id : id_item
64
+ self.create_time = payment_time #item.invoice.billing_period_from
60
65
  self.type = type
61
66
  if (type != MOVEMENT_TYPE_ADD_BONUS)
62
67
  self.paypal1_amount = item.amount.to_f
@@ -70,69 +75,127 @@ module BlackStack
70
75
  self.product_code = item.product_code
71
76
  self.profits_amount = 0
72
77
  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
78
+ if (type == MOVEMENT_TYPE_ADD_BONUS || type == MOVEMENT_TYPE_ADD_PAYMENT)
79
+ self.expiration_time = DB["SELECT DATEADD(#{prod[:credits_expiration_period].to_s}#{prod[:credits_expiration_period].to_s}, +#{prod[:credits_expiration_units].to_s}, '#{payment_time.to_sql}') AS d"].first[:d].to_s
80
+ end
81
+ self.expiration_on_next_payment = plan[:expiration_on_next_payment]
82
+ self.expiration_lead_period = plan[:expiration_lead_period]
83
+ self.expiration_lead_units = plan[:expiration_lead_units]
84
+ #self.give_away_negative_credits = plan[:give_away_negative_credits]
74
85
  self.save()
86
+ # recalculate - CANCELADO - SE DEBE HACER OFFLINE
87
+ #self.recalculate
88
+ #
89
+ self
75
90
  end
76
91
 
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
92
+ # Returns the number of credits assigned in the movement that have been consumed.
93
+ # The movment must be a payment or a bonus
94
+ def credits_consumed()
95
+ # the movment must be a payment or a bonus
96
+ raise 'Movement must be either a payment or a bonus' if self.type != MOVEMENT_TYPE_ADD_PAYMENT && self.type != MOVEMENT_TYPE_ADD_BONUS
97
+ #puts
98
+ #puts "product_code:#{self.product_code}:."
99
+ # itero los pagos y bonos hechos por este mismo producto, desde el primer dia hasta este movimiento.
100
+ paid = 0
101
+ self.client.movements.select { |o|
102
+ (o.type == MOVEMENT_TYPE_ADD_PAYMENT || o.type == MOVEMENT_TYPE_ADD_BONUS) &&
103
+ o.credits.to_f < 0 &&
104
+ o.product_code.upcase == self.product_code.upcase
105
+ }.sort_by { |o| o.create_time }.each { |o|
106
+ paid += (0.to_f - o.credits.to_f)
107
+ break if o.id.to_guid == self.id.to_guid
108
+ }
109
+ #puts "paid:#{paid.to_s}:."
110
+ # calculo los credito para este producto, desde el primer dia; incluyendo cosumo, expiraciones, ajustes.
111
+ consumed = self.client.movements.select { |o|
112
+ o.credits.to_f > 0 &&
113
+ o.product_code.upcase == self.product_code.upcase
114
+ }.inject(0) { |sum, o| sum.to_f + o.credits.to_f }.to_f
115
+ #puts "consumed:#{consumed.to_s}:."
116
+ # calculo los creditos de este movimiento que voy a cancelar
117
+ credits = 0.to_f - self.credits.to_f
118
+ #puts "credits:#{credits.to_s}:."
119
+ #
120
+ if paid - consumed <= 0 # # se consumio todo
121
+ #puts "a"
122
+ return credits
123
+ else # paid - consumed > 0 # todavia no se consumio todo
124
+ if paid - consumed > credits # todavia se estan consumiendo creditos de los pagos anteriores
125
+ #puts "b"
126
+ return 0
127
+ else # paid - consumed >= credits # se consumio una parte del credito
128
+ #puts "c"
129
+ n = credits >= (paid - consumed) ? credits - (paid - consumed) : credits
130
+ #puts "n:#{n.to_s}:."
131
+ return n
132
+ end
133
+ end
134
+ end
96
135
 
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
136
+ # returns the real expiration based on expiration_time, expiration_lead_period and expiration_lead_units
137
+ def expiration_lead_time()
138
+ return nil if self.expiration_time.nil?
139
+ return self.expiration_time if self.expiration_lead_period.nil? || self.expiration_lead_units.nil?
140
+ if self.expiration_lead_period == 'H' # hours
141
+ return self.expiration_time + self.expiration_lead_units.to_i * 60*60
142
+ elsif self.expiration_lead_period == 'D' # days
143
+ return self.expiration_time + self.expiration_lead_units.to_i * 24*60*60
144
+ elsif self.expiration_lead_period == 'W' # weeks
145
+ return self.expiration_time + self.expiration_lead_units.to_i * 7*24*60*60
146
+ elsif self.expiration_lead_period == 'M' # months
147
+ return self.expiration_time + self.expiration_lead_units.to_i * 31*24*60*60
148
+ elsif self.expiration_lead_period == 'Y' # years
149
+ return self.expiration_time + self.expiration_lead_units.to_i * 366*24*60*60
150
+ else
151
+ return self.expiration_time
152
+ end
153
+ end
103
154
 
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
155
+ # credits expiration
156
+ def expire()
157
+ credits_consumed = self.credits_consumed
158
+ #
159
+ self.expiration_start_time = now()
160
+ self.expiration_tries = self.expiration_tries.to_i + 1
161
+ self.save
162
+ #
163
+ total_credits = 0.to_f - BlackStack::Balance.new(self.client.id, self.product_code).credits.to_f
164
+ total_amount = 0.to_f - BlackStack::Balance.new(self.client.id, self.product_code).amount.to_f
165
+ #
166
+ credits = 0.to_i - self.credits.to_i
167
+ #
168
+ credits_to_expire = credits - credits_consumed.to_i #- (0.to_f - self.credits.to_f)).to_i
169
+ amount_to_expire = total_credits.to_f == 0 ? 0 : credits_to_expire.to_f * ( total_amount.to_f / total_credits.to_f )
170
+ #
171
+ exp = BlackStack::Movement.new
172
+ exp.id = guid()
173
+ exp.id_client = self.client.id
174
+ exp.create_time = now()
175
+ exp.type = BlackStack::Movement::MOVEMENT_TYPE_EXPIRATION
176
+ exp.id_user_creator = self.id_user_creator
177
+ exp.description = 'Expiration Because Allocation is Renewed'
178
+ exp.paypal1_amount = 0
179
+ exp.bonus_amount = 0
180
+ exp.amount = amount_to_expire
181
+ exp.credits = credits_to_expire
182
+ exp.profits_amount = -amount_to_expire
183
+ exp.id_invoice_item = self.id_invoice_item
184
+ exp.product_code = self.product_code
185
+ exp.save
186
+ #
187
+ self.expiration_end_time = now()
188
+ self.save
189
+ end # def expire
113
190
 
114
- # calculo el # de creditos no usados de este movimiento que ha expirado
115
- z = x-y>0 ? x-y : 0
191
+ # recalculate the amount for all the consumptions.
192
+ # The movment must be a payment or a bonus or refund
193
+ def recalculate()
194
+ # the movment must be a payment or a bonus or a refund
195
+ raise 'Movement must be either a payment or bonus or refund' if type != MOVEMENT_TYPE_ADD_PAYMENT && type != MOVEMENT_TYPE_ADD_BONUS && type != MOVEMENT_TYPE_REFUND_BALANCE
196
+ # recalculate amounts for all the consumptions and expirations
197
+ self.client.recalculate(self.product_code)
198
+ end
116
199
 
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
200
  end # class Movement
138
201
  end # module BlackStack
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: invoicing_payments_processing
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.4
4
+ version: 1.1.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leandro Daniel Sardi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-09 00:00:00.000000000 Z
11
+ date: 2020-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: websocket