from functools import reduce from datetime import datetime from rest_framework import mixins from rest_framework.viewsets import GenericViewSet from django.db.models import Q from .models import Bill, Payment, PaymentMethod, Order from django.utils import timezone from django.core.exceptions import ObjectDoesNotExist from calendar import monthrange def sum_amounts(entries): return reduce(lambda acc, entry: acc + entry.amount, entries, 0) def get_balance_for(user): bills = sum_amounts(Bill.objects.filter(owner=user)) payments = sum_amounts(Payment.objects.filter(owner=user)) return payments - bills def get_payment_method_for(user): methods = PaymentMethod.objects.filter(owner=user) for method in methods: # Do we want to do something with non-primary method? if method.primary: return method return None def beginning_of_month(year, month): tz = timezone.get_current_timezone() return datetime(year=year, month=month, day=1, tzinfo=tz) def end_of_month(year, month): (_, days) = monthrange(year, month) tz = timezone.get_current_timezone() return datetime(year=year, month=month, day=days, hour=23, minute=59, second=59, tzinfo=tz) def generate_bills_for(year, month, user, allowed_delay): # /!\ We exclusively work on the specified year and month. # Default values for next bill (if any). Only saved at the end of # this method, if relevant. next_bill = Bill(owner=user, starting_date=beginning_of_month(year, month), ending_date=end_of_month(year, month), creation_date=timezone.now(), due_date=timezone.now() + allowed_delay) # Select all orders active on the request period. orders = Order.objects.filter( Q(ending_date__gt=next_bill.starting_date) | Q(ending_date__isnull=True), owner=user) # Check if there is already a bill covering the order and period pair: # * Get latest bill by ending_date: previous_bill.ending_date # * If previous_bill.ending_date is before next_bill.ending_date, a new # bill has to be generated. unpaid_orders = [] for order in orders: try: previous_bill = order.bill.latest('ending_date') except ObjectDoesNotExist: previous_bill = None if previous_bill == None or previous_bill.ending_date < next_bill.ending_date: unpaid_orders.append(order) # Commit next_bill if it there are 'unpaid' orders. if len(unpaid_orders) > 0: next_bill.save() # It is not possible to register many-to-many relationship before # the two end-objects are saved in database. for order in unpaid_orders: order.bill.add(next_bill) # TODO: use logger. print("Generated bill {} (amount: {}) for user {}." .format(next_bill.uuid, next_bill.total, user)) return next_bill # Return None if no bill was created. class ProductViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.ListModelMixin, GenericViewSet): """ A customer-facing viewset that provides default `create()`, `retrieve()` and `list()`. """ pass