Continue to refactor for shifting logic into the order
This commit is contained in:
		
					parent
					
						
							
								077c665c53
							
						
					
				
			
			
				commit
				
					
						d8a7964fed
					
				
			
		
					 3 changed files with 161 additions and 91 deletions
				
			
		| 
						 | 
				
			
			@ -95,8 +95,8 @@ python manage.py migrate
 | 
			
		|||
**** Add it to DNS as vpn-XXX.ungleich.ch
 | 
			
		||||
**** Route a /40 network to its IPv6 address
 | 
			
		||||
**** Install wireguard on it
 | 
			
		||||
**** TODO Enable wireguard on boot
 | 
			
		||||
**** TODO Create a new VPNPool on uncloud with
 | 
			
		||||
**** TODO [#C] Enable wireguard on boot
 | 
			
		||||
**** TODO [#C] Create a new VPNPool on uncloud with
 | 
			
		||||
***** the network address (selecting from our existing pool)
 | 
			
		||||
***** the network size (/...)
 | 
			
		||||
***** the vpn host that provides the network (selecting the created VM)
 | 
			
		||||
| 
						 | 
				
			
			@ -210,3 +210,16 @@ VPNNetworks can be managed by all authenticated users.
 | 
			
		|||
 | 
			
		||||
*** Decision
 | 
			
		||||
    We use integers, because they are easy.
 | 
			
		||||
 | 
			
		||||
** Milestones                                                       :uncloud:
 | 
			
		||||
*** 1.1 (cleanup 1)
 | 
			
		||||
****
 | 
			
		||||
*** 1.0 (initial release)
 | 
			
		||||
**** TODO Initial Generic product support
 | 
			
		||||
     - Product
 | 
			
		||||
***** TODO Recurring product support
 | 
			
		||||
****** TODO Support replacing orders for updates
 | 
			
		||||
****** TODO [#A] Finish split of bill creation
 | 
			
		||||
****** TODO [#A] Test the new functions in the Order class
 | 
			
		||||
***** TODO Generating bill for admins/staff
 | 
			
		||||
      -
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -372,24 +372,23 @@ class Order(models.Model):
 | 
			
		|||
 | 
			
		||||
        return next_date
 | 
			
		||||
 | 
			
		||||
    def get_ending_date_for_bill(bill):
 | 
			
		||||
    def get_ending_date_for_bill(self, bill):
 | 
			
		||||
        """
 | 
			
		||||
        Determine the ending date given a specific bill
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # If the order is quit, charge the final amount (?)
 | 
			
		||||
        if order.ending_date:
 | 
			
		||||
            this_ending_date = order.ending_date
 | 
			
		||||
        if self.ending_date:
 | 
			
		||||
            this_ending_date = self.ending_date
 | 
			
		||||
        else:
 | 
			
		||||
            if order.earliest_ending_date > bill.ending_date:
 | 
			
		||||
                this_ending_date = order.earliest_ending_date
 | 
			
		||||
            if self.earliest_ending_date > bill.ending_date:
 | 
			
		||||
                this_ending_date = self.earliest_ending_date
 | 
			
		||||
            else:
 | 
			
		||||
                this_ending_date = bill.ending_date
 | 
			
		||||
 | 
			
		||||
        return this_ending_date
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def count_billed(self):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -458,6 +457,74 @@ class Order(models.Model):
 | 
			
		|||
 | 
			
		||||
        super().save(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def create_bill_record(self, bill):
 | 
			
		||||
        br = None
 | 
			
		||||
 | 
			
		||||
        if self.is_one_time:
 | 
			
		||||
            if self.billrecord_set.count() == 0:
 | 
			
		||||
                br = BillRecord.objects.create(bill=bill,
 | 
			
		||||
                                               order=self,
 | 
			
		||||
                                               starting_date=self.starting_date,
 | 
			
		||||
                                               ending_date=self.ending_date)
 | 
			
		||||
        else:
 | 
			
		||||
            br = BillRecord.objects.filter(bill=bill, order=self).first()
 | 
			
		||||
            if br:
 | 
			
		||||
                self.update_bill_record_for_recurring_order(br, bill)
 | 
			
		||||
 | 
			
		||||
            else:
 | 
			
		||||
                br = self.create_new_bill_record_for_recurring_order(bill)
 | 
			
		||||
 | 
			
		||||
        return br
 | 
			
		||||
 | 
			
		||||
    def update_bill_record_for_recurring_order(self,
 | 
			
		||||
                                         bill_record,
 | 
			
		||||
                                         bill):
 | 
			
		||||
        """
 | 
			
		||||
        Possibly update a bill record according to the information in the bill
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # If the order has an ending date set, we might need to adjust the bill_record
 | 
			
		||||
        if self.ending_date:
 | 
			
		||||
            if bill_record_for_this_bill.ending_date != self.ending_date:
 | 
			
		||||
                bill_record_for_this_bill.ending_date = self.ending_date
 | 
			
		||||
 | 
			
		||||
            else:
 | 
			
		||||
                # recurring, not terminated, should go until at least end of bill
 | 
			
		||||
                if bill_record_for_this_bill.ending_date < bill.ending_date:
 | 
			
		||||
                    bill_record_for_this_bill.ending_date = bill.ending_date
 | 
			
		||||
 | 
			
		||||
                bill_record_for_this_bill.save()
 | 
			
		||||
 | 
			
		||||
    def create_new_bill_record_for_recurring_order(self, bill):
 | 
			
		||||
        """
 | 
			
		||||
        Create a new bill record
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        last_bill_record = BillRecord.objects.filter(order=self).order_by('id').last()
 | 
			
		||||
 | 
			
		||||
        starting_date=self.starting_date
 | 
			
		||||
 | 
			
		||||
        if last_bill_record:
 | 
			
		||||
            # We already charged beyond the end of this bill's period
 | 
			
		||||
            if last_bill_record.ending_date >= bill.ending_date:
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
            # This order is terminated or replaced
 | 
			
		||||
            if self.ending_date:
 | 
			
		||||
                # And the last bill record already covered us -> nothing to be done anymore
 | 
			
		||||
                if last_bill_record.ending_date == self.ending_date:
 | 
			
		||||
                    return
 | 
			
		||||
 | 
			
		||||
            starting_date = start_after(last_bill_record.ending_date)
 | 
			
		||||
 | 
			
		||||
        ending_date = self.get_ending_date_for_bill(bill)
 | 
			
		||||
 | 
			
		||||
        return BillRecord.objects.create(bill=bill,
 | 
			
		||||
                                       order=self,
 | 
			
		||||
                                       starting_date=starting_date,
 | 
			
		||||
                                       ending_date=ending_date)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return f"{self.description} (order={self.id})"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -509,34 +576,12 @@ class Bill(models.Model):
 | 
			
		|||
    @classmethod
 | 
			
		||||
    def create_bills_for_all_users(cls):
 | 
			
		||||
        """
 | 
			
		||||
        Create bills for all users
 | 
			
		||||
        Create next bill for each user
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        for owner in get_user_model().objects.all():
 | 
			
		||||
            cls.create_next_bills_for_user(owner)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def create_next_bill_for_user_address(cls,
 | 
			
		||||
                                          owner,
 | 
			
		||||
                                          billing_address,
 | 
			
		||||
                                          ending_date=None):
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        Check the existing orders  of the billing address
 | 
			
		||||
        and generate a bill if there is at least one active order
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        all_orders = Order.objects.filter(owner=owner,
 | 
			
		||||
                                          billing_address=billing_address).order_by('id')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        # a bill. If there
 | 
			
		||||
        # for order in all_orders:
 | 
			
		||||
        #     if order.is_one_time:
 | 
			
		||||
        #         cls.create_one_time_record
 | 
			
		||||
 | 
			
		||||
        # pass
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def create_next_bills_for_user(cls, owner, ending_date=None):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -547,80 +592,93 @@ class Bill(models.Model):
 | 
			
		|||
        bills = []
 | 
			
		||||
 | 
			
		||||
        for billing_address in BillingAddress.objects.filter(owner=owner):
 | 
			
		||||
            bills.append(cls.create_next_bill_for_user_address(owner, billing_address, ending_date))
 | 
			
		||||
            bills.append(cls.create_next_bill_for_user_address(billing_address, ending_date))
 | 
			
		||||
 | 
			
		||||
        return bills
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_or_create_bill(cls, billing_address):
 | 
			
		||||
        last_bill = cls.objects.filter(billing_address=billing_address).order_by('id').last()
 | 
			
		||||
 | 
			
		||||
        all_orders = Order.objects.filter(billing_address=billing_address).order_by('id')
 | 
			
		||||
        first_order = all_orders.first()
 | 
			
		||||
 | 
			
		||||
        bill = None
 | 
			
		||||
        ending_date = None
 | 
			
		||||
 | 
			
		||||
        # Get date & bill from previous bill, if it exists
 | 
			
		||||
        if last_bill:
 | 
			
		||||
            if not last_bill.is_final:
 | 
			
		||||
                bill = last_bill
 | 
			
		||||
                starting_date = last_bill.starting_date
 | 
			
		||||
                ending_date = bill.ending_date
 | 
			
		||||
            else:
 | 
			
		||||
                starting_date = last_bill.ending_date + datetime.timedelta(seconds=1)
 | 
			
		||||
        else:
 | 
			
		||||
            # Might be an idea to make this the start of the month, too
 | 
			
		||||
            if first_order:
 | 
			
		||||
                starting_date = first_order.starting_date
 | 
			
		||||
            else:
 | 
			
		||||
                starting_date = timezone.now()
 | 
			
		||||
 | 
			
		||||
        if not ending_date:
 | 
			
		||||
            ending_date = end_of_month(starting_date)
 | 
			
		||||
 | 
			
		||||
        if not bill:
 | 
			
		||||
            bill = cls.objects.create(
 | 
			
		||||
                owner=billing_address.owner,
 | 
			
		||||
                starting_date=starting_date,
 | 
			
		||||
                ending_date=ending_date,
 | 
			
		||||
                billing_address=billing_address)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return bill
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def create_bill_records_for_recurring_orders(cls, bill):
 | 
			
		||||
    def create_next_bill_for_user_address(cls,
 | 
			
		||||
                                          billing_address,
 | 
			
		||||
                                          ending_date=None):
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        Create or update bill records for recurring orders for the
 | 
			
		||||
        given bill
 | 
			
		||||
        Create the next bill for a specific billing address of a user
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        owner = bill.owner
 | 
			
		||||
        billing_address = bill.billing_address
 | 
			
		||||
        owner = billing_address.owner
 | 
			
		||||
 | 
			
		||||
        all_orders = Order.objects.filter(owner=owner,
 | 
			
		||||
                                          billing_address=billing_address).order_by('id').exclude(recurring_period=RecurringPeriod.ONE_TIME)
 | 
			
		||||
                                          billing_address=billing_address).order_by('id')
 | 
			
		||||
 | 
			
		||||
        bill = cls.get_or_create_bill(billing_address)
 | 
			
		||||
 | 
			
		||||
        for order in all_orders:
 | 
			
		||||
            bill_record_for_this_bill = BillRecord.objects.filter(bill=bill,
 | 
			
		||||
                                                                  order=order).first()
 | 
			
		||||
            if bill_record_for_this_bill:
 | 
			
		||||
                cls.update_bill_record_for_this_bill(bill_record_for_this_bill,
 | 
			
		||||
                                                     order,
 | 
			
		||||
                                                     bill)
 | 
			
		||||
            order.create_bill_record(bill)
 | 
			
		||||
 | 
			
		||||
            else:
 | 
			
		||||
                cls.create_new_bill_record_for_this_bill(order, bill)
 | 
			
		||||
        return bill
 | 
			
		||||
 | 
			
		||||
    # @classmethod
 | 
			
		||||
    # def create_bill_records_for_recurring_orders(cls, bill):
 | 
			
		||||
    #     """
 | 
			
		||||
    #     Create or update bill records for recurring orders for the
 | 
			
		||||
    #     given bill
 | 
			
		||||
    #     """
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def create_new_bill_record_for_this_bill(order, bill):
 | 
			
		||||
        last_bill_record = BillRecord.objects.filter(order=order).order_by('id').last()
 | 
			
		||||
    #     owner = bill.owner
 | 
			
		||||
    #     billing_address = bill.billing_address
 | 
			
		||||
 | 
			
		||||
        this_starting_date=order.starting_date
 | 
			
		||||
    #     all_orders = Order.objects.filter(owner=owner,
 | 
			
		||||
    #                                       billing_address=billing_address).order_by('id').exclude(recurring_period=RecurringPeriod.ONE_TIME)
 | 
			
		||||
 | 
			
		||||
        if last_bill_record:
 | 
			
		||||
            if last_bill_record.ending_date >= bill.ending_date:
 | 
			
		||||
                return
 | 
			
		||||
    #     for order in all_orders:
 | 
			
		||||
    #         bill_record_for_this_bill = BillRecord.objects.filter(bill=bill,
 | 
			
		||||
    #                                                               order=order).first()
 | 
			
		||||
    #         if bill_record_for_this_bill:
 | 
			
		||||
    #             cls.update_bill_record_for_this_bill(bill_record_for_this_bill,
 | 
			
		||||
    #                                                  order,
 | 
			
		||||
    #                                                  bill)
 | 
			
		||||
 | 
			
		||||
            if order.ending_date:
 | 
			
		||||
                if last_bill_record.ending_date == order.ending_date:
 | 
			
		||||
                    return
 | 
			
		||||
    #         else:
 | 
			
		||||
    #             cls.create_new_bill_record_for_this_bill(order, bill)
 | 
			
		||||
 | 
			
		||||
            this_starting_date = start_after(last_bill_record.ending_date)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        ending_date = order.get_ending_date_for_bill(bill)
 | 
			
		||||
 | 
			
		||||
        return BillRecord.objects.create(bill=bill,
 | 
			
		||||
                                       order=order,
 | 
			
		||||
                                       starting_date=this_starting_date,
 | 
			
		||||
                                       ending_date=this_ending_date)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def update_bill_record_for_this_bill(bill_record,
 | 
			
		||||
                                         order,
 | 
			
		||||
                                         bill):
 | 
			
		||||
 | 
			
		||||
        # we may need to adjust it, but let's do this logic another time
 | 
			
		||||
 | 
			
		||||
        # If the order has an ending date set, we might need to adjust the bill_record
 | 
			
		||||
        if order.ending_date:
 | 
			
		||||
            if bill_record_for_this_bill.ending_date != order.ending_date:
 | 
			
		||||
                bill_record_for_this_bill.ending_date = order.ending_date
 | 
			
		||||
 | 
			
		||||
            else:
 | 
			
		||||
                # recurring, not terminated, should go until at least end of bill
 | 
			
		||||
                if bill_record_for_this_bill.ending_date < bill.ending_date:
 | 
			
		||||
                    bill_record_for_this_bill.ending_date = bill.ending_date
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                bill_record_for_this_bill.save()
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def create_next_bill_for_user_address_old(cls,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -269,7 +269,7 @@ class BillTestCase(TestCase):
 | 
			
		|||
        Ensure there is only 1 bill record per order
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        bill = Bill.create_next_bill_for_user_address(self.user, self.user_addr)
 | 
			
		||||
        bill = Bill.create_next_bill_for_user_address(self.user_addr)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.one_time_order.billrecord_set.count(), 1)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -278,7 +278,7 @@ class BillTestCase(TestCase):
 | 
			
		|||
        Check the bill sum for a single one time order
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        bill = Bill.create_next_bill_for_user_address(self.user, self.user_addr)
 | 
			
		||||
        bill = Bill.create_next_bill_for_user_address(self.user_addr)
 | 
			
		||||
        self.assertEqual(bill.sum, self.order_meta[1]['price'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -287,8 +287,7 @@ class BillTestCase(TestCase):
 | 
			
		|||
        Ensure there is only 1 bill record per order
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        bill = Bill.create_next_bill_for_user_address(self.recurring_user,
 | 
			
		||||
                                                      self.recurring_user_addr)
 | 
			
		||||
        bill = Bill.create_next_bill_for_user_address(self.recurring_user_addr)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.recurring_order.billrecord_set.count(), 1)
 | 
			
		||||
        self.assertEqual(bill.billrecord_set.count(), 1)
 | 
			
		||||
| 
						 | 
				
			
			@ -301,7 +300,7 @@ class BillTestCase(TestCase):
 | 
			
		|||
        """
 | 
			
		||||
 | 
			
		||||
        for ending_date in self.bill_dates:
 | 
			
		||||
            b = Bill.create_next_bill_for_user_address(self.recurring_user, self.recurring_user_addr, ending_date)
 | 
			
		||||
            b = Bill.create_next_bill_for_user_address(self.recurring_user_addr, ending_date)
 | 
			
		||||
            b.close()
 | 
			
		||||
 | 
			
		||||
        bill_count = Bill.objects.filter(owner=self.recurring_user).count()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue