def update_response_paid_status(response):
    """
    Update response paid status and apply updates, as necessary.
    """
    response.paid = float(response.amount_paid) >= float(response.paymentInfo.get("total", 0))
    if response.pending_update:
        response.paid = float(response.amount_paid) >= float(response.pending_update["paymentInfo"].get("total", 0))
        if response.paid:
            response.value = response.pending_update["value"]
            response.paymentInfo = response.pending_update["paymentInfo"]
            response.pending_update = None
            response.update_trail.append(UpdateTrailItem(date=datetime.datetime.now(), update_type="apply_update"))
    return response.paid
Пример #2
0
def update_response_path(response, path, value, response_base_path):
    from ..main import app
    existing_value = get(getattr(response, response_base_path), path, value)
    if type(value) is str:
        if type(existing_value) is bool and value.lower() in ("true", "false"):
            value = value.lower() == "true"
        elif type(existing_value) is float and value.isdigit():
            value = float(value)
        elif type(existing_value) is int and value.isdigit():
            value = int(value)
    set_(getattr(response, response_base_path), path, value)
    response.update_trail.append(
        UpdateTrailItem(path=path,
                        old_value=existing_value,
                        new_value=value,
                        date=datetime.datetime.now(),
                        user=app.get_current_user_id(),
                        response_base_path=response_base_path))
Пример #3
0
def form_response_new(formId):
    """
    Payload: 
    {
        "data": formData,
        "modifyLink": "...",
        "responseId"?: "asdsadasd"
    }
    If responseId is defined, it is an update.
    Otherwise, it is an existing submission.
    """
    from ..main import app
    email_sent = False
    responseId = app.current_request.json_body.get("responseId", None)
    if not responseId:
        responseId = ObjectId()
        newResponse = True
    else:
        responseId = ObjectId(responseId)
        newResponse = False

    form = Form.objects.get({"_id": ObjectId(formId)})
    response_data = app.current_request.json_body["data"]
    response_data = process_response_data_images(response_data)
    postprocess = form.formOptions.postprocess
    if postprocess and "patches" in postprocess and type(
            postprocess["patches"]) is list:
        response_data = patch_predicate(response_data, postprocess["patches"])
    counter_value = None
    counter = form.formOptions.counter
    if newResponse and counter and "enabled" in counter and counter[
            "enabled"] == True:
        counter_value = get_counter(formId)
    modify_link = app.current_request.json_body.get('modifyLink', '')
    paymentInfo = form.formOptions.paymentInfo
    confirmationEmailInfo = form.formOptions.confirmationEmailInfo
    paymentMethods = fill_paymentMethods_with_data(
        form.formOptions.paymentMethods, response_data)

    def calc_item_total_to_paymentInfo(paymentInfoItem, paymentInfo):
        paymentInfoItem['amount'] = calculate_price(
            paymentInfoItem.get('amount', '0'), response_data)
        paymentInfoItem['quantity'] = calculate_price(
            paymentInfoItem.get('quantity', '0'), response_data)
        paymentInfo[
            'total'] += paymentInfoItem['amount'] * paymentInfoItem['quantity']
        if "couponCode" in paymentInfoItem and paymentInfoItem[
                "amount"] * paymentInfoItem["quantity"] != 0:
            slots_maximum = calculate_price(
                paymentInfoItem.get("couponCodeMaximum", "-1"), response_data)
            if slots_maximum != -1:
                slots_requested = calculate_price(
                    paymentInfoItem.get("couponCodeCount", "1"), response_data)
                slots_used = form.couponCodes_used.get(
                    paymentInfoItem["couponCode"], 0)
                slots_available = slots_maximum - slots_used
                slots_remaining = slots_available - slots_requested
                if slots_remaining < 0:
                    message = "Coupon code maximum reached.\nSubmitting this form will cause you to exceed the coupon code maximum.\nNumber of spots remaining: {}".format(
                        int(slots_available))
                    return False, {
                        "res": {
                            "success": False,
                            "message": message,
                            "fields_to_clear": ["couponCode"]
                        }
                    }
                form.couponCodes_used[paymentInfoItem[
                    "couponCode"]] = slots_used + slots_requested
                Form.objects.raw({
                    "_id": form.id
                }).update({
                    "$set": {
                        f"couponCodes_used.{paymentInfoItem['couponCode']}":
                        slots_used + slots_requested
                    }
                })
        return True, {}

    paymentInfoItemsWithTotal = []
    paymentInfoItemsInstallment = []
    paymentInfo['total'] = 0
    for paymentInfoItem in paymentInfo.setdefault('items', []):
        paymentInfoItem.setdefault("name", "Payment Item")
        paymentInfoItem.setdefault("description", "Payment Item")
        paymentInfoItem.setdefault("quantity", "1")
        if paymentInfoItem.get("installment", False) == True:
            # Don't count "installment" payments towards the total.
            paymentInfoItemsInstallment.append(paymentInfoItem)
            continue
        if "$total" in paymentInfoItem.get(
                "amount", "0") or "$total" in paymentInfoItem.get(
                    "quantity", "0"):
            # Take care of this at the end.
            paymentInfoItemsWithTotal.append(paymentInfoItem)
            continue
        success, error = calc_item_total_to_paymentInfo(
            paymentInfoItem, paymentInfo)
        if success is False:
            return error

    # Now take care of items for round off, etc. -- which need the total value to work.
    response_data["total"] = float(paymentInfo["total"])
    for paymentInfoItem in paymentInfoItemsWithTotal:
        success, error = calc_item_total_to_paymentInfo(
            paymentInfoItem, paymentInfo)
        if success is False:
            return error

    # Take care of installment payments now.
    response_data["total"] = float(paymentInfo["total"])
    for paymentInfoItem in paymentInfoItemsInstallment:
        paymentInfoItem['amount'] = calculate_price(
            paymentInfoItem.get('amount', '0'), response_data)
        paymentInfoItem['quantity'] = calculate_price(
            paymentInfoItem.get('quantity', '0'), response_data)

    response_data.pop("total", None)

    paymentInfo['items'] = [
        item for item in paymentInfo['items']
        if item['quantity'] * item['amount'] != 0
    ]
    userId = app.get_current_user_id()
    paid = paymentInfo["total"] == 0
    if newResponse:
        response = Response(form=form,
                            id=responseId,
                            date_created=datetime.datetime.now(),
                            modify_link=modify_link + "?responseId=" +
                            str(responseId) if modify_link else "",
                            counter=counter_value)
        if get(form, "formOptions.loginRequired", False
               ) is True and userId is not "cm:cognitoUserPool:anonymousUser":
            user = get_user_or_create_one(userId)
            response.user = userId
            # Only one response per user.
            try:
                Response.objects.get({
                    "form": ObjectId(formId),
                    "user": userId
                })
                raise Exception(
                    f"Response with userId {userId} already exists!")
            except DoesNotExist:
                pass
    else:
        response = Response.objects.get({"_id": responseId})
        response.update_trail.append(
            UpdateTrailItem(old=response.value,
                            new=response_data,
                            date=datetime.datetime.now(),
                            update_type="update"))
        if (response.paid == True
                and paymentInfo["total"] <= response.paymentInfo["total"]):
            paid = True
        if form.id != response.form.id:
            raise UnauthorizedError(
                f"Response {response.id} does not belong to form {form.id}; it belongs to form {response.form.id}."
            )
        if response.user and response.user.id != userId:
            app.check_permissions(form, 'Responses_Edit')
            # raise UnauthorizedError(f"User {userId} does not own response {response.id} (owner is {response.user.id})")
    if newResponse or (not newResponse and paid):
        response.value = response_data
        response.date_modified = datetime.datetime.now()
        response.paymentInfo = paymentInfo
        response.paid = paid
        if not newResponse:
            response.update_trail.append(
                UpdateTrailItem(date=datetime.datetime.now(),
                                update_type="apply_update"))
        if paid and confirmationEmailInfo:  # If total amount is zero (user uses coupon code to get for free)
            send_confirmation_email(response, confirmationEmailInfo)
            email_sent = True
        # todo: fix this, should auto_email come even if not paid?
        if "auto_email" in paymentMethods and get(
                paymentMethods, "auto_email.enabled", True) == True and type(
                    get(paymentMethods, "autoEmail.confirmationEmailInfo") is
                    dict):
            send_confirmation_email(
                response,
                get(paymentMethods, "auto_email.confirmationEmailInfo"))
            email_sent = True
        response.save()
        if "description" in paymentInfo and type(
                paymentInfo["description"]) is str:
            paymentInfo["description"] = fill_string_from_template(
                response, paymentInfo["description"])
        if "ccavenue" in paymentMethods and response.paid == False:
            paymentMethods["ccavenue"] = update_ccavenue_hash(
                formId, paymentMethods["ccavenue"], response)
        return {
            "res": {
                "value": response_data,
                "paid": paid,
                "success": True,
                "action": "insert",
                "email_sent": email_sent,
                "responseId": str(responseId),
                "paymentInfo": paymentInfo,
                "paymentMethods": paymentMethods
            }
        }
    elif not newResponse:
        # Update.
        response.date_modified = datetime.datetime.now()
        # Not using pending_update for now.
        # response.pending_update = {
        #     "value": response_data,
        #     "paymentInfo": paymentInfo,
        # }
        response.value = response_data
        response.paymentInfo = paymentInfo
        response.paid = paid
        response.save()
        if "description" in paymentInfo and type(
                paymentInfo["description"]) is str:
            paymentInfo["description"] = fill_string_from_template(
                response, paymentInfo["description"])
        if "ccavenue" in paymentMethods and response.paid == False:
            paymentMethods["ccavenue"] = update_ccavenue_hash(
                formId, paymentMethods["ccavenue"], response)
        return {
            "res": {
                "value": response_data,
                "paid": paid,
                "success": True,
                "action": "update",
                "email_sent": email_sent,
                "responseId": str(responseId),
                "paymentInfo": paymentInfo,
                "paymentMethods": paymentMethods,
                "amt_received": {
                    "currency": paymentInfo["currency"],
                    "total": float(response.amount_paid or 0)
                }
            }
        }