2020-02-28 07:59:32 +00:00
|
|
|
from functools import reduce
|
2020-02-29 08:08:30 +00:00
|
|
|
from datetime import datetime
|
2020-02-28 13:57:45 +00:00
|
|
|
from rest_framework import mixins
|
|
|
|
from rest_framework.viewsets import GenericViewSet
|
2020-02-29 08:08:30 +00:00
|
|
|
from django.db.models import Q
|
|
|
|
from .models import Bill, Payment, PaymentMethod, Order
|
|
|
|
from django.utils import timezone
|
2020-03-01 11:23:04 +00:00
|
|
|
from django.core.exceptions import ObjectDoesNotExist
|
2020-03-01 14:47:27 +00:00
|
|
|
from calendar import monthrange
|
2020-02-28 07:59:32 +00:00
|
|
|
|
|
|
|
def get_balance_for(user):
|
2020-03-02 09:46:04 +00:00
|
|
|
bills = reduce(
|
|
|
|
lambda acc, entry: acc + entry.total,
|
|
|
|
Bill.objects.filter(owner=user),
|
|
|
|
0)
|
|
|
|
payments = reduce(
|
|
|
|
lambda acc, entry: acc + entry.amount,
|
|
|
|
Payment.objects.filter(owner=user),
|
|
|
|
0)
|
2020-02-28 08:10:36 +00:00
|
|
|
return payments - bills
|
2020-02-28 07:59:32 +00:00
|
|
|
|
2020-02-28 08:10:36 +00:00
|
|
|
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
|
2020-02-28 13:57:45 +00:00
|
|
|
|
2020-03-01 14:47:27 +00:00
|
|
|
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)
|
2020-02-29 08:08:30 +00:00
|
|
|
|
|
|
|
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,
|
2020-03-01 14:47:27 +00:00
|
|
|
starting_date=beginning_of_month(year, month),
|
|
|
|
ending_date=end_of_month(year, month),
|
2020-02-29 08:08:30 +00:00
|
|
|
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:
|
2020-03-01 14:47:27 +00:00
|
|
|
previous_bill = order.bill.latest('ending_date')
|
2020-02-29 08:08:30 +00:00
|
|
|
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)
|
|
|
|
|
2020-03-01 14:47:27 +00:00
|
|
|
# TODO: use logger.
|
|
|
|
print("Generated bill {} (amount: {}) for user {}."
|
|
|
|
.format(next_bill.uuid, next_bill.total, user))
|
|
|
|
|
2020-02-29 08:08:30 +00:00
|
|
|
return next_bill
|
|
|
|
|
|
|
|
# Return None if no bill was created.
|
2020-02-28 13:57:45 +00:00
|
|
|
|
|
|
|
class ProductViewSet(mixins.CreateModelMixin,
|
|
|
|
mixins.RetrieveModelMixin,
|
|
|
|
mixins.ListModelMixin,
|
|
|
|
GenericViewSet):
|
|
|
|
"""
|
|
|
|
A customer-facing viewset that provides default `create()`, `retrieve()`
|
|
|
|
and `list()`.
|
|
|
|
"""
|
|
|
|
pass
|