Changeset - 8d66ed57150d
[Not reviewed]
0 6 0
Christopher Neugebauer - 8 years ago 2016-03-25 03:51:39
chrisjrn@gmail.com
Fix flake8 warnings
6 files changed with 22 insertions and 14 deletions:
0 comments (0 inline, 0 general)
registrasion/forms.py
Show inline comments
 
import models as rego
 

	
 
from controllers.product import ProductController
 

	
 
from django import forms
 

	
 

	
 
def CategoryForm(category):
 

	
 
    PREFIX = "product_"
 

	
 
    def field_name(product):
 
        return PREFIX + ("%d" % product.id)
 

	
 
    class _CategoryForm(forms.Form):
 

	
 
        @staticmethod
 
        def initial_data(product_quantities):
 
            ''' Prepares initial data for an instance of this form.
 
            product_quantities is a sequence of (product,quantity) tuples '''
 
            initial = {}
 
            for product, quantity in product_quantities:
 
                initial[field_name(product)] = quantity
 

	
 
            return initial
 

	
 
        def product_quantities(self):
 
            ''' Yields a sequence of (product, quantity) tuples from the
 
            cleaned form data. '''
 
            for name, value in self.cleaned_data.items():
 
                if name.startswith(PREFIX):
 
                    product_id = int(name[len(PREFIX):])
 
                    yield (product_id, value, name)
 

	
 
        def disable_product(self, product):
 
            ''' Removes a given product from this form. '''
 
            del self.fields[field_name(product)]
 

	
 
        def disable_products_for_user(self, user):
 
            for product in products:
 
                # Remove fields that do not have an enabling condition.
 
                prod = ProductController(product)
 
                if not prod.can_add_with_enabling_conditions(user, 0):
 
                    self.disable_product(product)
 

	
 

	
 
    products = rego.Product.objects.filter(category=category).order_by("order")
 
    for product in products:
 

	
 
        help_text = "$%d -- %s" % (product.price, product.description)
 

	
 
        field = forms.IntegerField(
 
            label=product.name,
 
            help_text=help_text,
 
        )
 
        _CategoryForm.base_fields[field_name(product)] = field
 

	
 
    return _CategoryForm
 

	
 

	
 
class ProfileForm(forms.ModelForm):
 
    ''' A form for requesting badge and profile information. '''
 

	
 
    class Meta:
 
        model = rego.BadgeAndProfile
 
        exclude = ['attendee']
 

	
 

	
 
class VoucherForm(forms.Form):
 
    voucher = forms.CharField(
 
        label="Voucher code",
 
        help_text="If you have a voucher code, enter it here",
 
        required=False,
 
    )
registrasion/templatetags/registrasion_tags.py
Show inline comments
 
from registrasion import models as rego
 

	
 
from django import template
 

	
 
register = template.Library()
 

	
 

	
 
@register.assignment_tag(takes_context=True)
 
def available_categories(context):
 
    ''' Returns all of the available product categories '''
 
    return rego.Category.objects.all()
registrasion/tests/test_discount.py
Show inline comments
...
 
@@ -19,198 +19,198 @@ class DiscountTestCase(RegistrationCartTestCase):
 
        )
 
        discount.save()
 
        discount.enabling_products.add(cls.PROD_1)
 
        discount.save()
 
        rego.DiscountForProduct.objects.create(
 
            discount=discount,
 
            product=cls.PROD_2,
 
            percentage=amount,
 
            quantity=2
 
        ).save()
 
        return discount
 

	
 
    @classmethod
 
    def add_discount_prod_1_includes_cat_2(
 
            cls,
 
            amount=Decimal(100),
 
            quantity=2):
 
        discount = rego.IncludedProductDiscount.objects.create(
 
            description="PROD_1 includes CAT_2 " + str(amount) + "%",
 
        )
 
        discount.save()
 
        discount.enabling_products.add(cls.PROD_1)
 
        discount.save()
 
        rego.DiscountForCategory.objects.create(
 
            discount=discount,
 
            category=cls.CAT_2,
 
            percentage=amount,
 
            quantity=quantity,
 
        ).save()
 
        return discount
 

	
 
    def test_discount_is_applied(self):
 
        self.add_discount_prod_1_includes_prod_2()
 

	
 
        cart = CartController.for_user(self.USER_1)
 
        cart.add_to_cart(self.PROD_1, 1)
 
        cart.add_to_cart(self.PROD_2, 1)
 

	
 
        # Discounts should be applied at this point...
 
        self.assertEqual(1, len(cart.cart.discountitem_set.all()))
 

	
 
    def test_discount_is_applied_for_category(self):
 
        self.add_discount_prod_1_includes_cat_2()
 

	
 
        cart = CartController.for_user(self.USER_1)
 
        cart.add_to_cart(self.PROD_1, 1)
 
        cart.add_to_cart(self.PROD_3, 1)
 

	
 
        # Discounts should be applied at this point...
 
        self.assertEqual(1, len(cart.cart.discountitem_set.all()))
 

	
 
    def test_discount_does_not_apply_if_not_met(self):
 
        self.add_discount_prod_1_includes_prod_2()
 

	
 
        cart = CartController.for_user(self.USER_1)
 
        cart.add_to_cart(self.PROD_2, 1)
 

	
 
        # No discount should be applied as the condition is not met
 
        self.assertEqual(0, len(cart.cart.discountitem_set.all()))
 

	
 
    def test_discount_applied_out_of_order(self):
 
        self.add_discount_prod_1_includes_prod_2()
 

	
 
        cart = CartController.for_user(self.USER_1)
 
        cart.add_to_cart(self.PROD_2, 1)
 
        cart.add_to_cart(self.PROD_1, 1)
 

	
 
        # No discount should be applied as the condition is not met
 
        self.assertEqual(1, len(cart.cart.discountitem_set.all()))
 

	
 
    def test_discounts_collapse(self):
 
        self.add_discount_prod_1_includes_prod_2()
 

	
 
        cart = CartController.for_user(self.USER_1)
 
        cart.add_to_cart(self.PROD_1, 1)
 
        cart.add_to_cart(self.PROD_2, 1)
 
        cart.add_to_cart(self.PROD_2, 1)
 

	
 
        # Discounts should be applied and collapsed at this point...
 
        self.assertEqual(1, len(cart.cart.discountitem_set.all()))
 

	
 
    def test_discounts_respect_quantity(self):
 
        self.add_discount_prod_1_includes_prod_2()
 

	
 
        cart = CartController.for_user(self.USER_1)
 
        cart.add_to_cart(self.PROD_1, 1)
 
        cart.add_to_cart(self.PROD_2, 3)
 

	
 
        # There should be three items in the cart, but only two should
 
        # attract a discount.
 
        discount_items = list(cart.cart.discountitem_set.all())
 
        self.assertEqual(2, discount_items[0].quantity)
 

	
 
    def test_multiple_discounts_apply_in_order(self):
 
        discount_full = self.add_discount_prod_1_includes_prod_2()
 
        discount_half = self.add_discount_prod_1_includes_prod_2(Decimal(50))
 

	
 
        cart = CartController.for_user(self.USER_1)
 
        cart.add_to_cart(self.PROD_1, 1)
 
        cart.add_to_cart(self.PROD_2, 3)
 

	
 
        # There should be two discounts
 
        discount_items = list(cart.cart.discountitem_set.all())
 
        discount_items.sort(key=lambda item: item.quantity)
 
        self.assertEqual(2, len(discount_items))
 
        # The half discount should be applied only once
 
        self.assertEqual(1, discount_items[0].quantity)
 
        self.assertEqual(discount_half.pk, discount_items[0].discount.pk)
 
        # The full discount should be applied twice
 
        self.assertEqual(2, discount_items[1].quantity)
 
        self.assertEqual(discount_full.pk, discount_items[1].discount.pk)
 

	
 
    def test_discount_applies_across_carts(self):
 
        self.add_discount_prod_1_includes_prod_2()
 

	
 
        # Enable the discount during the first cart.
 
        cart = CartController.for_user(self.USER_1)
 
        cart.add_to_cart(self.PROD_1, 1)
 
        cart.cart.active = False
 
        cart.cart.save()
 

	
 
        # Use the discount in the second cart
 
        cart = CartController.for_user(self.USER_1)
 
        cart.add_to_cart(self.PROD_2, 1)
 

	
 
        # The discount should be applied.
 
        self.assertEqual(1, len(cart.cart.discountitem_set.all()))
 
        cart.cart.active = False
 
        cart.cart.save()
 

	
 
        # The discount should respect the total quantity across all
 
        # of the user's carts.
 
        cart = CartController.for_user(self.USER_1)
 
        cart.add_to_cart(self.PROD_2, 2)
 

	
 
        # Having one item in the second cart leaves one more item where
 
        # the discount is applicable. The discount should apply, but only for
 
        # quantity=1
 
        discount_items = list(cart.cart.discountitem_set.all())
 
        self.assertEqual(1, discount_items[0].quantity)
 

	
 
    def test_discount_applies_only_once_enabled(self):
 
        # Enable the discount during the first cart.
 
        cart = CartController.for_user(self.USER_1)
 
        cart.add_to_cart(self.PROD_1, 1)
 
        # This would exhaust discount if present
 
        cart.add_to_cart(self.PROD_2, 2)
 
        cart.cart.active = False
 
        cart.cart.save()
 

	
 
        self.add_discount_prod_1_includes_prod_2()
 
        cart = CartController.for_user(self.USER_1)
 
        cart.add_to_cart(self.PROD_2, 2)
 

	
 
        discount_items = list(cart.cart.discountitem_set.all())
 
        self.assertEqual(2, discount_items[0].quantity)
 

	
 
    def test_category_discount_applies_once_per_category(self):
 
        self.add_discount_prod_1_includes_cat_2(quantity=1)
 
        cart = CartController.for_user(self.USER_1)
 
        cart.add_to_cart(self.PROD_1, 1)
 

	
 
        # Add two items from category 2
 
        cart.add_to_cart(self.PROD_3, 1)
 
        cart.add_to_cart(self.PROD_4, 1)
 

	
 
        discount_items = list(cart.cart.discountitem_set.all())
 
        # There is one discount, and it should apply to one item.
 
        self.assertEqual(1, len(discount_items))
 
        self.assertEqual(1, discount_items[0].quantity)
 

	
 
    def test_category_discount_applies_to_highest_value(self):
 
        self.add_discount_prod_1_includes_cat_2(quantity=1)
 
        cart = CartController.for_user(self.USER_1)
 
        cart.add_to_cart(self.PROD_1, 1)
 

	
 
        # Add two items from category 2, add the less expensive one first
 
        cart.add_to_cart(self.PROD_4, 1)
 
        cart.add_to_cart(self.PROD_3, 1)
 

	
 
        discount_items = list(cart.cart.discountitem_set.all())
 
        # There is one discount, and it should apply to the more expensive.
 
        self.assertEqual(1, len(discount_items))
 
        self.assertEqual(self.PROD_3, discount_items[0].product)
 

	
 
    def test_discount_quantity_is_per_user(self):
 
        self.add_discount_prod_1_includes_cat_2(quantity=1)
 

	
 
        # Both users should be able to apply the same discount
 
        # in the same way
 
        for user in (self.USER_1, self.USER_2):
 
            cart = CartController.for_user(user)
 
            cart.add_to_cart(self.PROD_1, 1) # Enable the discount
 
            cart.add_to_cart(self.PROD_1, 1)  # Enable the discount
 
            cart.add_to_cart(self.PROD_3, 1)
 

	
 
            discount_items = list(cart.cart.discountitem_set.all())
 
            # The discount is applied.
 
            self.assertEqual(1, len(discount_items))
registrasion/tests/test_voucher.py
Show inline comments
 
import datetime
 
import pytz
 

	
 
from decimal import Decimal
 
from django.core.exceptions import ValidationError
 
from django.db import IntegrityError
 

	
 
from registrasion import models as rego
 
from registrasion.controllers.cart import CartController
 

	
 
from test_cart import RegistrationCartTestCase
 

	
 
UTC = pytz.timezone('UTC')
 

	
 

	
 
class VoucherTestCases(RegistrationCartTestCase):
 

	
 
    @classmethod
 
    def new_voucher(self, code="VOUCHER"):
 
        voucher = rego.Voucher.objects.create(
 
            recipient="Voucher recipient",
 
            code=code,
 
            limit=1
 
        )
 
        voucher.save()
 
        return voucher
 

	
 
    def test_apply_voucher(self):
 
        voucher = self.new_voucher()
 

	
 
        self.set_time(datetime.datetime(2015, 01, 01, tzinfo=UTC))
 

	
 
        cart_1 = CartController.for_user(self.USER_1)
 
        cart_1.apply_voucher(voucher.code)
 
        self.assertIn(voucher, cart_1.cart.vouchers.all())
 

	
 
        # Second user should not be able to apply this voucher (it's exhausted)
 
        cart_2 = CartController.for_user(self.USER_2)
 
        with self.assertRaises(ValidationError):
 
            cart_2.apply_voucher(voucher.code)
 

	
 
        # After the reservation duration
 
        # user 2 should be able to apply voucher
 
        self.add_timedelta(rego.Voucher.RESERVATION_DURATION * 2)
 
        cart_2.apply_voucher(voucher.code)
 
        cart_2.cart.active = False
 
        cart_2.cart.save()
 

	
 
        # After the reservation duration, user 1 should not be able to apply
 
        # voucher, as user 2 has paid for their cart.
 
        self.add_timedelta(rego.Voucher.RESERVATION_DURATION * 2)
 
        with self.assertRaises(ValidationError):
 
            cart_1.apply_voucher(voucher.code)
 

	
 
    def test_voucher_enables_item(self):
 
        voucher = self.new_voucher()
 

	
 
        enabling_condition = rego.VoucherEnablingCondition.objects.create(
 
            description="Voucher condition",
 
            voucher=voucher,
 
            mandatory=False,
 
        )
 
        enabling_condition.save()
 
        enabling_condition.products.add(self.PROD_1)
 
        enabling_condition.save()
 

	
 
        # Adding the product without a voucher will not work
 
        current_cart = CartController.for_user(self.USER_1)
 
        with self.assertRaises(ValidationError):
 
            current_cart.add_to_cart(self.PROD_1, 1)
 

	
 
        # Apply the voucher
 
        current_cart.apply_voucher(voucher.code)
 
        current_cart.add_to_cart(self.PROD_1, 1)
 

	
 
    def test_voucher_enables_discount(self):
 
        voucher = self.new_voucher()
 

	
 
        discount = rego.VoucherDiscount.objects.create(
 
            description="VOUCHER RECIPIENT",
 
            voucher=voucher,
 
        )
 
        discount.save()
 
        rego.DiscountForProduct.objects.create(
 
            discount=discount,
 
            product=self.PROD_1,
 
            percentage=Decimal(100),
 
            quantity=1
 
        ).save()
 

	
 
        # Having PROD_1 in place should add a discount
 
        current_cart = CartController.for_user(self.USER_1)
 
        current_cart.apply_voucher(voucher.code)
 
        current_cart.add_to_cart(self.PROD_1, 1)
 
        self.assertEqual(1, len(current_cart.cart.discountitem_set.all()))
 

	
 
    def test_voucher_codes_unique(self):
 
        voucher1 = self.new_voucher(code="VOUCHER")
 
        self.new_voucher(code="VOUCHER")
 
        with self.assertRaises(IntegrityError):
 
            voucher2 = self.new_voucher(code="VOUCHER")
 
            self.new_voucher(code="VOUCHER")
 

	
 
    def test_multiple_vouchers_work(self):
 
        voucher1 = self.new_voucher(code="VOUCHER1")
 
        voucher2 = self.new_voucher(code="VOUCHER2")
 
        self.new_voucher(code="VOUCHER1")
 
        self.new_voucher(code="VOUCHER2")
 

	
 
    def test_vouchers_case_insensitive(self):
 
        voucher = self.new_voucher(code="VOUCHeR")
 
        current_cart = CartController.for_user(self.USER_1)
 
        current_cart.apply_voucher(voucher.code.lower())
registrasion/urls.py
Show inline comments
 
from django.conf.urls import url, patterns
 

	
 
urlpatterns = patterns(
 
    "registrasion.views",
 
    url(r"^category/([0-9]+)$", "product_category", name="product_category"),
 
    url(r"^checkout$", "checkout", name="checkout"),
 
    url(r"^invoice/([0-9]+)$", "invoice", name="invoice"),
 
    url(r"^invoice/([0-9]+)/pay$", "pay_invoice", name="pay_invoice"),
 
    url(r"^profile$", "edit_profile", name="profile"),
 
    url(r"^register$", "guided_registration", name="guided_registration"),
 
    url(r"^register/([0-9]+)$", "guided_registration", name="guided_registration"),
 
    url(r"^register/([0-9]+)$", "guided_registration",
 
        name="guided_registration"),
 
)
registrasion/views.py
Show inline comments
 
from registrasion import forms
 
from registrasion import models as rego
 
from registrasion.controllers.cart import CartController
 
from registrasion.controllers.invoice import InvoiceController
 
from registrasion.controllers.product import ProductController
 

	
 
from django.contrib.auth.decorators import login_required
 
from django.core.exceptions import ObjectDoesNotExist
 
from django.core.exceptions import ValidationError
 
from django.db import transaction
 
from django.shortcuts import redirect
 
from django.shortcuts import render
 

	
 

	
 
@login_required
 
def guided_registration(request, page_id=0):
 
    ''' Goes through the registration process in order,
 
    making sure user sees all valid categories.
 

	
 
    WORK IN PROGRESS: the finalised version of this view will allow
 
    grouping of categories into a specific page. Currently, it just goes
 
    through each category one by one
 
    '''
 

	
 
    dashboard = redirect("dashboard")
 
    next_step = redirect("guided_registration")
 

	
 
    attendee = rego.Attendee.get_instance(request.user)
 
    if attendee.completed_registration:
 
        return dashboard
 

	
 
    # Step 1: Fill in a badge
 
    profile = rego.BadgeAndProfile.get_instance(attendee)
 

	
 
    if profile is None:
 
        ret = edit_profile(request)
 
        profile_new = rego.BadgeAndProfile.get_instance(attendee)
 
        if profile_new is None:
 
            # No new profile was created
 
            return ret
 
        else:
 
            return next_step
 

	
 
    # Step 2: Go through each of the categories in order
 
    category = attendee.highest_complete_category
 

	
 
    # Get the next category
 
    cats = rego.Category.objects
 
    cats = cats.filter(id__gt=category).order_by("order")
 

	
 
    if len(cats) == 0:
 
        # We've filled in every category
 
        attendee.completed_registration = True
 
        attendee.save()
 
        return dashboard
 

	
 
    ret = product_category(request, cats[0].id)
 
    attendee_new = rego.Attendee.get_instance(request.user)
 
    if attendee_new.highest_complete_category == category:
 
        # We've not yet completed this category
 
        return ret
 
    else:
 
        return next_step
 

	
 

	
 
@login_required
 
def edit_profile(request):
 
    attendee = rego.Attendee.get_instance(request.user)
 

	
 
    try:
 
        profile = rego.BadgeAndProfile.objects.get(attendee=attendee)
 
    except ObjectDoesNotExist:
 
        profile = None
 

	
 
    form = forms.ProfileForm(request.POST or None, instance=profile)
 

	
 
    if request.POST and form.is_valid():
 
        form.instance.attendee = attendee
 
        form.save()
 

	
 
    data = {
 
        "form": form,
 
    }
 
    return render(request, "profile_form.html", data)
 

	
 

	
 
@login_required
 
def product_category(request, category_id):
 
    ''' Registration selections form for a specific category of items.
 
    '''
 

	
 
    PRODUCTS_FORM_PREFIX = "products"
 
    VOUCHERS_FORM_PREFIX = "vouchers"
 

	
 
    category_id = int(category_id)  # Routing is [0-9]+
 
    category = rego.Category.objects.get(pk=category_id)
 
    current_cart = CartController.for_user(request.user)
 

	
 
    CategoryForm = forms.CategoryForm(category)
 

	
 
    attendee = rego.Attendee.get_instance(request.user)
 

	
 
    products = rego.Product.objects.filter(category=category)
 
    products = products.order_by("order")
 

	
 
    if request.method == "POST":
 
        cat_form = CategoryForm(request.POST, request.FILES, prefix=PRODUCTS_FORM_PREFIX)
 
        cat_form = CategoryForm(
 
            request.POST,
 
            request.FILES,
 
            prefix=PRODUCTS_FORM_PREFIX)
 
        cat_form.disable_products_for_user(request.user)
 
        voucher_form = forms.VoucherForm(request.POST, prefix=VOUCHERS_FORM_PREFIX)
 
        voucher_form = forms.VoucherForm(
 
            request.POST,
 
            prefix=VOUCHERS_FORM_PREFIX)
 

	
 
        if voucher_form.is_valid() and voucher_form.cleaned_data["voucher"].strip():
 
        if (voucher_form.is_valid() and
 
                voucher_form.cleaned_data["voucher"].strip()):
 
            # Apply voucher
 
            # leave
 
            voucher = voucher_form.cleaned_data["voucher"]
 
            try:
 
                current_cart.apply_voucher(voucher)
 
            except Exception as e:
 
                voucher_form.add_error("voucher", e)
 
            # Re-visit current page.
 
        elif cat_form.is_valid():
 
            try:
 
                handle_valid_cat_form(cat_form, current_cart)
 
            except ValidationError as ve:
 
            except ValidationError:
 
                pass
 

	
 
            # If category is required, the user must have at least one
 
            # in an active+valid cart
 

	
 
            if category.required:
 
                carts = rego.Cart.reserved_carts()
 
                carts = carts.filter(user=request.user)
 
                items = rego.ProductItem.objects.filter(
 
                    product__category=category,
 
                    cart=carts,
 
                )
 
                if len(items) == 0:
 
                    cat_form.add_error(
 
                        None,
 
                        "You must have at least one item from this category",
 
                    )
 

	
 
            if not cat_form.errors:
 
                if category_id > attendee.highest_complete_category:
 
                    attendee.highest_complete_category = category_id
 
                    attendee.save()
 
                return redirect("dashboard")
 

	
 
    else:
 
        # Create initial data for each of products in category
 
        items = rego.ProductItem.objects.filter(
 
            product__category=category,
 
            cart=current_cart.cart,
 
        )
 
        quantities = []
 
        for product in products:
 
            # Only add items that are enabled.
 
            prod = ProductController(product)
 
            try:
 
                quantity = items.get(product=product).quantity
 
            except ObjectDoesNotExist:
 
                quantity = 0
 
            quantities.append((product, quantity))
 

	
 
        initial = CategoryForm.initial_data(quantities)
 
        cat_form = CategoryForm(prefix=PRODUCTS_FORM_PREFIX, initial=initial)
 
        cat_form.disable_products_for_user(request.user)
 

	
 
        voucher_form = forms.VoucherForm(prefix=VOUCHERS_FORM_PREFIX)
 

	
 

	
 
    data = {
 
        "category": category,
 
        "form": cat_form,
 
        "voucher_form": voucher_form,
 
    }
 

	
 
    return render(request, "product_category.html", data)
 

	
 

	
 
@transaction.atomic
 
def handle_valid_cat_form(cat_form, current_cart):
 
    for product_id, quantity, field_name in cat_form.product_quantities():
 
        product = rego.Product.objects.get(pk=product_id)
 
        try:
 
            current_cart.set_quantity(product, quantity, batched=True)
 
        except ValidationError as ve:
 
            cat_form.add_error(field_name, ve)
 
    if cat_form.errors:
 
        raise ValidationError("Cannot add that stuff")
 
    current_cart.end_batch()
 

	
 

	
 
@login_required
 
def checkout(request):
 
    ''' Runs checkout for the current cart of items, ideally generating an
 
    invoice. '''
 

	
 
    current_cart = CartController.for_user(request.user)
 
    current_invoice = InvoiceController.for_cart(current_cart.cart)
 

	
 
    return redirect("invoice", current_invoice.invoice.id)
 

	
 

	
 
@login_required
 
def invoice(request, invoice_id):
 
    ''' Displays an invoice for a given invoice id. '''
 

	
 
    invoice_id = int(invoice_id)
 
    inv = rego.Invoice.objects.get(pk=invoice_id)
 
    current_invoice = InvoiceController(inv)
 

	
 
    data = {
 
        "invoice": current_invoice.invoice,
 
    }
 

	
 
    return render(request, "invoice.html", data)
 

	
 

	
 
@login_required
 
def pay_invoice(request, invoice_id):
 
    ''' Marks the invoice with the given invoice id as paid.
 
    WORK IN PROGRESS FUNCTION. Must be replaced with real payment workflow.
 

	
 
    '''
 

	
 
    invoice_id = int(invoice_id)
 
    inv = rego.Invoice.objects.get(pk=invoice_id)
 
    current_invoice = InvoiceController(inv)
 
    if not inv.paid and current_invoice.is_valid():
 
        current_invoice.pay("Demo invoice payment", inv.value)
 

	
 
    return redirect("invoice", current_invoice.invoice.id)
0 comments (0 inline, 0 general)