create_subscriptions_invoices.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. # -*- coding: utf-8 -*-
  2. import datetime
  3. from decimal import Decimal
  4. from dateutil.relativedelta import relativedelta
  5. from django.http import HttpResponse
  6. from django.db import transaction
  7. from django.db.models import Q
  8. from coin.offers.models import Offer, OfferSubscription
  9. from coin.members.models import Member
  10. from coin.billing.models import Invoice, InvoiceDetail
  11. def create_all_members_invoices_for_a_period(date=datetime.date.today()):
  12. """
  13. Pour chaque membre ayant au moins un abonnement actif, génère les factures
  14. en prenant la date comme premier mois de la période de facturation
  15. """
  16. members = Member.objects.filter(
  17. Q(offersubscription__resign_date__isnull=True) |
  18. Q(offersubscription__resign_date__gte=date))
  19. for member in members:
  20. create_member_invoice_for_a_period(member,date)
  21. @transaction.atomic
  22. def create_member_invoice_for_a_period(member, date):
  23. """
  24. Créé si necessaire une facture pour un membre en prenant la date passée
  25. en paramètre comme premier mois de période. Renvoi la facture générée
  26. ou None si aucune facture n'était necessaire.
  27. """
  28. sid = transaction.savepoint()
  29. date_first_of_month = datetime.date(date.year, date.month, 1)
  30. date_last_of_month = (date_first_of_month + relativedelta(months=+1) -
  31. relativedelta(days=+1))
  32. invoice = Invoice.objects.create(
  33. date_due=datetime.date.today(),
  34. member=member
  35. )
  36. # Récupère les abonnements actifs du membre à la fin du mois
  37. offer_subscriptions = member.get_active_subscriptions(date_last_of_month)
  38. # Pour chaque abonnement
  39. for offer_subscription in offer_subscriptions:
  40. # Récupère l'offre de l'abonnement
  41. offer = offer_subscription.offer
  42. # Vérifie s'il s'agit de la première facture d'un abonnement,
  43. # Alors facture en plus les frais de mise en service
  44. invoicedetail_test_first = InvoiceDetail.objects.filter(
  45. offersubscription__exact=offer_subscription.pk,
  46. invoice__member__exact=member.pk)
  47. if not invoicedetail_test_first.exists():
  48. invoice.details.create(
  49. label=offer.name + " - Frais de mise en service",
  50. amount=offer.initial_fees,
  51. offersubscription=offer_subscription,
  52. period_from=None,
  53. period_to=None)
  54. # Période de facturation de l'item par defaut
  55. # - Du début du mois de la date passée en paramètre
  56. # - Jusqu'à la fin du mois de la période de facturation de l'offre
  57. period_from = date_first_of_month
  58. period_to = (date_first_of_month +
  59. relativedelta(months=+offer.billing_period) -
  60. relativedelta(days=+1))
  61. planned_period_number_of_days = (period_to - period_from).days + 1
  62. quantity = 1
  63. # Si la facture est le premier mois de l'abonnement, alors met la
  64. # date de début de facturation au jour de l'ouverture de
  65. # l'abonnement
  66. if date_first_of_month == datetime.date(
  67. offer_subscription.subscription_date.year,
  68. offer_subscription.subscription_date.month, 1):
  69. period_from = offer_subscription.subscription_date
  70. # Recherche dans les factures déjà existantes de ce membre des
  71. # items ayant cet abonnement pour lesquels la période de
  72. # facturation englobe le début de notre période de facturation
  73. # actuelle
  74. invoicedetail_test_before = InvoiceDetail.objects.filter(
  75. offersubscription__exact=offer_subscription.pk,
  76. period_from__lte=period_from,
  77. period_to__gt=period_from,
  78. invoice__member__exact=member.pk)
  79. # Si une facture de ce genre existe alors ne fait rien.
  80. if not invoicedetail_test_before.exists():
  81. # Recherche dans les factures déjà existantes de ce membre des
  82. # items ayant cet abonnement pour lesquels la période de
  83. # facturation commence avant la fin de notre période de facturation
  84. # actuelle
  85. invoicedetail_test_after = InvoiceDetail.objects.filter(
  86. offersubscription__exact=offer_subscription.pk,
  87. period_from__lte=period_to,
  88. period_from__gte=period_from,
  89. invoice__member__exact=member.pk)
  90. # Si une telle facture existe, récupère la date de début de
  91. # facturation pour en faire la date de fin de facturation
  92. if invoicedetail_test_after.exists():
  93. invoicedetail_after = invoicedetail_test_after.first()
  94. period_to = (
  95. datetime.date(invoicedetail_after.period_from.year,
  96. invoicedetail_after.period_from.month, 1) -
  97. relativedelta(days=+1))
  98. # Si la période de facturation varie par rapport à celle prévue par
  99. # l'offre, calcul au prorata en faisant varier la quantité
  100. period_number_of_days = (period_to - period_from).days + 1
  101. if planned_period_number_of_days != period_number_of_days:
  102. quantity = (Decimal(period_number_of_days) /
  103. Decimal(planned_period_number_of_days))
  104. # Si durée de 0jours ou dates incohérentes, alors on ajoute pas
  105. # (Si la period est de 0jours c'est que la facture existe déjà.)
  106. if period_from<period_to:
  107. # Ajout l'item de l'offre correspondant à l'abonnement
  108. # à la facture
  109. invoice.details.create(label=offer.name,
  110. amount=offer.period_fees,
  111. quantity=quantity,
  112. offersubscription=offer_subscription,
  113. period_from=period_from,
  114. period_to=period_to)
  115. # S'il n'y a pas d'items dans la facture, ne commit pas la transaction.
  116. if invoice.details.count() > 0:
  117. invoice.save()
  118. transaction.savepoint_commit(sid)
  119. return invoice
  120. else:
  121. transaction.savepoint_rollback(sid)
  122. return None