# Path: billing/actions/accounts.py
# def create_invoices(account_id: str, due_date: date) -> Sequence[Invoice]:
#     """
#     Creates the invoices for any due positive charges in the account.
#     If there are due positive charges in different currencies, one invoice is created for each currency.
# 
#     :param account_id: The account to invoice.
#     :param due_date: The due date for any invoice that gets created.
#     :return: A possibly-empty list of Invoices.
#     """
#     invoices = []
#     with transaction.atomic():
#         due_charges = Charge.objects \
#             .uninvoiced(account_id=account_id) \
#             .charges()
#         total = total_amount(due_charges)
#         for amount_due in total.monies():
#             if amount_due.amount > 0:
#                 invoice = Invoice.objects.create(account_id=account_id, due_date=due_date)
#                 Charge.objects \
#                     .uninvoiced(account_id=account_id) \
#                     .charges() \
#                     .in_currency(currency=amount_due.currency) \
#                     .update(invoice=invoice)
#                 invoices.append(invoice)
#     logger.info('created-invoices', account_id=str(account_id), invoice_ids=[i.pk for i in invoices])
#     for invoice in invoices:
#         invoice_ready.send(sender=create_invoices, invoice=invoice)
#     return invoices
# 
# Path: billing/models.py
# class Account(Model):
#     OPEN = 'OPEN'
#     CLOSED = 'CLOSED'
#     STATUS_CHOICES = (
#         (OPEN, _('Open')),
#         (CLOSED, _('Closed')),
#     )
#     id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
#     created = models.DateTimeField(auto_now_add=True, db_index=True)
#     modified = models.DateTimeField(auto_now=True)
#     owner = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='billing_account', on_delete=PROTECT)
#     currency = CurrencyField(db_index=True)
#     status = FSMField(max_length=20, choices=STATUS_CHOICES, default=OPEN, db_index=True)
#     # An account is marked as delinquent when it is registered then when the user
#     # registers a valid credit card it will be marked as compliant
#     delinquent = models.BooleanField(default=True, db_index=True)
# 
#     objects = AccountQuerySet.as_manager()
# 
#     def __str__(self):
#         return str(self.owner)
# 
#     @transition(field=status, source=OPEN, target=CLOSED)
#     def close(self):
#         pass
# 
#     @transition(field=status, source=CLOSED, target=OPEN)
#     def reopen(self):
#         pass
# 
#     def balance(self, as_of: date = None):
#         charges = Charge.objects.filter(account=self)
#         transactions = Transaction.successful.filter(account=self)
#         if as_of is not None:
#             charges = charges.filter(created__lte=as_of)
#             transactions = transactions.filter(created__lte=as_of)
#         return total_amount(transactions) - total_amount(charges)
# 
#     def is_solvent(
#         self,
#         currency_threshold_price_map: Dict[str, Decimal],
#         account_valid_cc_map: Dict[UUID, bool] = None,
#         account_balance_map: DefaultDict[UUID, DefaultDict[str, Decimal]] = None
#     ):
#         """
#         Given a map of currency thresholds determines if the account is solvent
# 
#         An account is solvent when:
#             * Has a valid and active credit card to pay OR
#             * Has enough balance to pay
# 
#         In currency_threshold_price_map param you have to specify the amount threshold
#         for each currency. If the account has enough of one of the currencies then
#         is solvent. Ex:
#         currency_threshold_price_map {
#             'CHF': Decimal(10.83),
#             'EUR': Decimal(10.),
#             'NOK': Decimal(103.97)
#         }
# 
#         Note: account_valid_cc_map and account_balance_map can be passed from outside
#               in order to improve the efficiency when we require to know if several
#               accounts are solvent
#         """
#         if not account_valid_cc_map:
#             from .actions.accounts import get_account_valid_credit_card_map
#             account_valid_cc_map = get_account_valid_credit_card_map(
#                 Account.objects.filter(id=self.id)
#             )
# 
#         if not account_balance_map:
#             from .actions.accounts import get_account_balance_map
#             accounts = Account.objects.filter(id=self.id)
#             account_balance_map = get_account_balance_map(accounts)
# 
#         return (
#             account_valid_cc_map[self.id] or
#             self.has_enough_balance(account_balance_map, currency_threshold_price_map)
#         )
# 
#     def has_enough_balance(
#         self,
#         account_balance_map: DefaultDict[UUID, DefaultDict[str, Decimal]],
#         currency_threshold_price_map: Dict[str, Decimal]
#     ) -> bool:
#         for currency, balance in account_balance_map[self.id].items():
#             if balance >= currency_threshold_price_map[currency]:
#                 return True
#         return False
# 
#     def has_usable_credit_cards(self) -> bool:
#         credit_cards = CreditCard.objects.filter(
#             account=self,
#             status=CreditCard.ACTIVE
#         ).valid()
#         return credit_cards.exists()


import logging
import progressbar
import structlog
from datetime import timedelta, date
from collections import defaultdict
from django.core.management.base import BaseCommand
from django.utils import timezone, dateparse
from ...actions.accounts import create_invoices
from ...models import Account




def set_debug(logger_name):
    logger = logging.getLogger(logger_name)
    logger.setLevel(logging.DEBUG)
    logger.addHandler(logging.StreamHandler())


logger = structlog.get_logger()


def parse_due_date(s):
    d = dateparse.parse_date(s)
    if d is None:
        raise ('Not a valid iso8601 date: {}'.format(s))
    return d



# Path: billing/actions/accounts.py
# def create_invoices(account_id: str, due_date: date) -> Sequence[Invoice]:
#     """
#     Creates the invoices for any due positive charges in the account.
#     If there are due positive charges in different currencies, one invoice is created for each currency.
# 
#     :param account_id: The account to invoice.
#     :param due_date: The due date for any invoice that gets created.
#     :return: A possibly-empty list of Invoices.
#     """
#     invoices = []
#     with transaction.atomic():
#         due_charges = Charge.objects \
#             .uninvoiced(account_id=account_id) \
#             .charges()
#         total = total_amount(due_charges)
#         for amount_due in total.monies():
#             if amount_due.amount > 0:
#                 invoice = Invoice.objects.create(account_id=account_id, due_date=due_date)
#                 Charge.objects \
#                     .uninvoiced(account_id=account_id) \
#                     .charges() \
#                     .in_currency(currency=amount_due.currency) \
#                     .update(invoice=invoice)
#                 invoices.append(invoice)
#     logger.info('created-invoices', account_id=str(account_id), invoice_ids=[i.pk for i in invoices])
#     for invoice in invoices:
#         invoice_ready.send(sender=create_invoices, invoice=invoice)
#     return invoices
# 
# Path: billing/models.py
# class Account(Model):
#     OPEN = 'OPEN'
#     CLOSED = 'CLOSED'
#     STATUS_CHOICES = (
#         (OPEN, _('Open')),
#         (CLOSED, _('Closed')),
#     )
#     id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
#     created = models.DateTimeField(auto_now_add=True, db_index=True)
#     modified = models.DateTimeField(auto_now=True)
#     owner = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='billing_account', on_delete=PROTECT)
#     currency = CurrencyField(db_index=True)
#     status = FSMField(max_length=20, choices=STATUS_CHOICES, default=OPEN, db_index=True)
#     # An account is marked as delinquent when it is registered then when the user
#     # registers a valid credit card it will be marked as compliant
#     delinquent = models.BooleanField(default=True, db_index=True)
# 
#     objects = AccountQuerySet.as_manager()
# 
#     def __str__(self):
#         return str(self.owner)
# 
#     @transition(field=status, source=OPEN, target=CLOSED)
#     def close(self):
#         pass
# 
#     @transition(field=status, source=CLOSED, target=OPEN)
#     def reopen(self):
#         pass
# 
#     def balance(self, as_of: date = None):
#         charges = Charge.objects.filter(account=self)
#         transactions = Transaction.successful.filter(account=self)
#         if as_of is not None:
#             charges = charges.filter(created__lte=as_of)
#             transactions = transactions.filter(created__lte=as_of)
#         return total_amount(transactions) - total_amount(charges)
# 
#     def is_solvent(
#         self,
#         currency_threshold_price_map: Dict[str, Decimal],
#         account_valid_cc_map: Dict[UUID, bool] = None,
#         account_balance_map: DefaultDict[UUID, DefaultDict[str, Decimal]] = None
#     ):
#         """
#         Given a map of currency thresholds determines if the account is solvent
# 
#         An account is solvent when:
#             * Has a valid and active credit card to pay OR
#             * Has enough balance to pay
# 
#         In currency_threshold_price_map param you have to specify the amount threshold
#         for each currency. If the account has enough of one of the currencies then
#         is solvent. Ex:
#         currency_threshold_price_map {
#             'CHF': Decimal(10.83),
#             'EUR': Decimal(10.),
#             'NOK': Decimal(103.97)
#         }
# 
#         Note: account_valid_cc_map and account_balance_map can be passed from outside
#               in order to improve the efficiency when we require to know if several
#               accounts are solvent
#         """
#         if not account_valid_cc_map:
#             from .actions.accounts import get_account_valid_credit_card_map
#             account_valid_cc_map = get_account_valid_credit_card_map(
#                 Account.objects.filter(id=self.id)
#             )
# 
#         if not account_balance_map:
#             from .actions.accounts import get_account_balance_map
#             accounts = Account.objects.filter(id=self.id)
#             account_balance_map = get_account_balance_map(accounts)
# 
#         return (
#             account_valid_cc_map[self.id] or
#             self.has_enough_balance(account_balance_map, currency_threshold_price_map)
#         )
# 
#     def has_enough_balance(
#         self,
#         account_balance_map: DefaultDict[UUID, DefaultDict[str, Decimal]],
#         currency_threshold_price_map: Dict[str, Decimal]
#     ) -> bool:
#         for currency, balance in account_balance_map[self.id].items():
#             if balance >= currency_threshold_price_map[currency]:
#                 return True
#         return False
# 
#     def has_usable_credit_cards(self) -> bool:
#         credit_cards = CreditCard.objects.filter(
#             account=self,
#             status=CreditCard.ACTIVE
#         ).valid()
#         return credit_cards.exists()


# Path: billing/management/commands/create_invoices.py
import logging
import progressbar
import structlog
from datetime import timedelta, date
from collections import defaultdict
from django.core.management.base import BaseCommand
from django.utils import timezone, dateparse
from ...actions.accounts import create_invoices
from ...models import Account





def set_debug(logger_name):
    logger = logging.getLogger(logger_name)
    logger.setLevel(logging.DEBUG)
    logger.addHandler(logging.StreamHandler())


logger = structlog.get_logger()


def parse_due_date(s):
    d = dateparse.parse_date(s)
    if d is None:
        raise ('Not a valid iso8601 date: {}'.format(s))
    return d



class Command(BaseCommand):