Exemplo n.º 1
0
def edit_invoice_details(order):
    """
    Update invoice with buyer's address and taxid
    """
    invoice_dict = request.json.get('invoice')
    if not request.json or not invoice_dict:
        return api_error(message=_(u"Missing invoice details"),
                         status_code=400)

    invoice = Invoice.query.get(request.json.get('invoice_id'))
    if invoice.is_final:
        return api_error(message=_(
            u"This invoice has been finalised and hence cannot be modified"),
                         status_code=400)

    invoice_form = InvoiceForm.from_json(invoice_dict, meta={'csrf': False})
    if not invoice_form.validate():
        return api_error(message=_(u"Incorrect invoice details"),
                         status_code=400,
                         errors=invoice_form.errors)
    else:
        invoice_form.populate_obj(invoice)
        db.session.commit()
        return api_success(result={
            'message': 'Invoice updated',
            'invoice': jsonify_invoice(invoice)
        },
                           doc=_(u"Invoice details added"),
                           status_code=201)
Exemplo n.º 2
0
def kharcha():
    """
    Accepts JSON containing an array of line_items, with the quantity and item_id set for each line_item.

    Returns JSON of line items in the format:
    {item_id: {'quantity': Y, 'final_amount': Z, 'discounted_amount': Z, 'discount_policy_ids': ['d1', 'd2']}}
    """
    if not request.json or not request.json.get('line_items'):
        return api_error(message='Missing line items', status_code=400)
    line_item_forms = LineItemForm.process_list(request.json.get('line_items'))
    if not line_item_forms:
        return api_error(message='Missing line items', status_code=400)

    # Make line item splits and compute amounts and discounts
    line_items = LineItem.calculate(
        [{
            'item_id': li_form.data.get('item_id')
        } for li_form in line_item_forms
         for x in range(li_form.data.get('quantity'))],
        coupons=sanitize_coupons(request.json.get('discount_coupons')))
    items_json = jsonify_line_items(line_items)
    order_final_amount = sum([
        values['final_amount'] for values in items_json.values()
        if values['final_amount'] is not None
    ])
    return jsonify(line_items=items_json,
                   order={'final_amount': order_final_amount})
Exemplo n.º 3
0
def process_partial_refund_for_order(order, form_dict):
    form = RefundTransactionForm.from_json(form_dict, meta={'csrf': False})
    if form.validate():
        requested_refund_amount = form.amount.data
        if not order.paid_amount:
            return api_error(
                message='Refunds can only be issued for paid orders',
                status_code=403,
                errors=['non cancellable'])

        if (order.refunded_amount +
                requested_refund_amount) > order.paid_amount:
            return api_error(
                message=
                'Invalid refund amount, must be lesser than net amount paid for the order',
                status_code=403,
                errors=['excess partial refund'])

        payment = OnlinePayment.query.filter_by(
            order=order,
            pg_payment_status=RAZORPAY_PAYMENT_STATUS.CAPTURED).one()
        rp_resp = razorpay.refund_payment(payment.pg_paymentid,
                                          requested_refund_amount)
        if rp_resp.status_code == 200:
            rp_refund = rp_resp.json()
            transaction = PaymentTransaction(
                order=order,
                transaction_type=TRANSACTION_TYPE.REFUND,
                online_payment=payment,
                currency=CURRENCY.INR,
                pg_refundid=rp_refund['id'],
                refunded_at=func.utcnow())
            form.populate_obj(transaction)
            db.session.add(transaction)
            db.session.commit()
            send_order_refund_mail.delay(order.id, transaction.amount,
                                         transaction.note_to_user)
            return api_success(result={'order_net_amount': order.net_amount},
                               doc=_(u"Refund processed for order"),
                               status_code=200)
        else:
            raise PaymentGatewayError(
                "Refund failed for order - {order} with the following details - {msg}"
                .format(order=order.id, msg=rp_resp.content), 424,
                "Refund failed. Please try again or contact support at {email}."
                .format(email=order.organization.contact_email))
    else:
        return api_error(message='Invalid input',
                         status_code=403,
                         errors=form.errors)
Exemplo n.º 4
0
def free(order):
    """
    Completes a order which has a final_amount of 0
    """
    order_amounts = order.get_amounts(LINE_ITEM_STATUS.PURCHASE_ORDER)
    if order_amounts.final_amount == 0:
        order.confirm_sale()
        db.session.add(order)
        db.session.commit()
        for line_item in order.line_items:
            line_item.confirm()
            db.session.add(line_item)
            if line_item.discount_coupon:
                line_item.discount_coupon.update_used_count()
                db.session.add(line_item.discount_coupon)
        db.session.commit()
        send_receipt_mail.delay(
            order.id,
            subject="{item_collection_title}: Your registration is confirmed!".
            format(item_collection_title=order.item_collection.title),
            template='free_order_confirmation_mail.html.jinja2')
        return api_success(result={'order_id': order.id},
                           doc=_(u"Free order confirmed"),
                           status_code=201)

    else:
        return api_error(message="Free order confirmation failed",
                         status_code=402)
Exemplo n.º 5
0
def process_partial_refund_for_order(data_dict):
    order = data_dict['order']
    form = data_dict['form']
    request_method = data_dict['request_method']

    if request_method == 'GET':
        return jsonify(form_template=render_form(form=form, title=u"Partial refund", submit=u"Refund", with_chrome=False))
    if form.validate_on_submit():
        requested_refund_amount = form.amount.data
        payment = OnlinePayment.query.filter_by(order=order, pg_payment_status=RAZORPAY_PAYMENT_STATUS.CAPTURED).one()
        rp_resp = razorpay.refund_payment(payment.pg_paymentid, requested_refund_amount)
        rp_refund = rp_resp.json()
        if rp_resp.status_code == 200:
            transaction = PaymentTransaction(order=order, transaction_type=TRANSACTION_TYPE.REFUND,
                online_payment=payment, currency=CURRENCY.INR, pg_refundid=rp_refund['id'],
                refunded_at=func.utcnow())
            form.populate_obj(transaction)
            db.session.add(transaction)
            db.session.commit()
            send_order_refund_mail.queue(order.id, transaction.amount, transaction.note_to_user)
            return api_success(result={'order_net_amount': order.net_amount},
                doc=_(u"Refund processed for order"), status_code=200)
        else:
            raise PaymentGatewayError("Refund failed for order - {order} with the following details - {msg}".format(order=order.id,
                msg=rp_refund['error']['description']), 424,
            "Refund failed. {reason}. Please try again or contact support at {email}.".format(reason=rp_refund['error']['description'], email=order.organization.contact_email))
    else:
        return api_error(message='Invalid input',
            status_code=403,
            errors=form.errors)
Exemplo n.º 6
0
def admin_edit_discount_policy(discount_policy):
    discount_policy_error_msg = _(
        u"The discount could not be updated. Please rectify the indicated issues"
    )
    if discount_policy.is_price_based and discount_policy.items:
        discount_policy_form = PriceBasedDiscountPolicyForm(
            obj=discount_policy, model=DiscountPolicy)
        discount_price = Price.query.filter_by(
            item=discount_policy.items[0],
            discount_policy=discount_policy).one()
        discount_price_form = DiscountPriceForm(obj=discount_price,
                                                model=Price,
                                                parent=discount_policy)
        if not discount_price_form.validate_on_submit():
            return api_error(message=_(
                u"There was an issue with the price. Please rectify the indicated issues"
            ),
                             status_code=400,
                             errors=discount_price_form.errors)
        discount_price_form.populate_obj(discount_price)
        if discount_policy.items and discount_price.item is not discount_policy.items[
                0]:
            discount_policy.items = [discount_price.item]
    elif discount_policy.is_coupon:
        discount_policy_form = CouponBasedDiscountPolicyForm(
            obj=discount_policy, model=DiscountPolicy)
    elif discount_policy.is_automatic:
        discount_policy_form = AutomaticDiscountPolicyForm(
            obj=discount_policy, model=DiscountPolicy)
    else:
        return api_error(message=_(u"Incorrect discount type"),
                         status_code=400)

    if discount_policy_form.validate_on_submit():
        discount_policy_form.populate_obj(discount_policy)
        db.session.commit()
        return api_success(result={
            'discount_policy':
            jsonify_discount_policy(discount_policy)
        },
                           doc="Discount policy updated.",
                           status_code=200)
    else:
        return api_error(message=discount_policy_error_msg,
                         status_code=400,
                         errors=discount_policy_form.errors)
Exemplo n.º 7
0
def payment(order):
    """
    Accepts JSON containing `pg_paymentid`.

    Creates a payment object, attempts to 'capture' the payment from Razorpay,
    and returns a JSON containing the result of the operation.

    A successful capture results in a `payment_transaction` registered against the order.
    """
    if not request.json.get('pg_paymentid'):
        return api_error(message="Missing payment id", status_code=400)

    order_amounts = order.get_amounts(LINE_ITEM_STATUS.PURCHASE_ORDER)
    online_payment = OnlinePayment(
        pg_paymentid=request.json.get('pg_paymentid'), order=order)

    rp_resp = razorpay.capture_payment(online_payment.pg_paymentid,
                                       order_amounts.final_amount)
    if rp_resp.status_code == 200:
        online_payment.confirm()
        db.session.add(online_payment)
        # Only INR is supported as of now
        transaction = PaymentTransaction(order=order,
                                         online_payment=online_payment,
                                         amount=order_amounts.final_amount,
                                         currency=CURRENCY.INR)
        db.session.add(transaction)
        order.confirm_sale()
        db.session.add(order)
        invoice_organization = order.organization.invoicer if order.organization.invoicer else order.organization
        invoice = Invoice(order=order, organization=invoice_organization)
        db.session.add(invoice)
        db.session.commit()
        for line_item in order.line_items:
            line_item.confirm()
            db.session.add(line_item)
            if line_item.discount_coupon:
                line_item.discount_coupon.update_used_count()
                db.session.add(line_item.discount_coupon)
        db.session.commit()
        send_receipt_mail.delay(
            order.id,
            subject=
            "{item_collection_title}: Thank you for your order (#{invoice_no})!"
            .format(item_collection_title=order.item_collection.title,
                    invoice_no=order.invoice_no))
        return api_success(result={'invoice_id': invoice.id},
                           doc=_(u"Payment verified"),
                           status_code=201)
    else:
        online_payment.fail()
        db.session.add(online_payment)
        db.session.commit()
        raise PaymentGatewayError(
            "Online payment failed for order - {order} with the following details - {msg}"
            .format(order=order.id, msg=rp_resp.content), 424,
            'Your payment failed. Please try again or contact us at {email}.'.
            format(email=order.organization.contact_email))
Exemplo n.º 8
0
def cancel_line_item(line_item):
    if not line_item.is_cancellable():
        return api_error(message='This ticket is not cancellable',
                    status_code=403,
                    errors=['non cancellable'])

    refund_amount = process_line_item_cancellation(line_item)
    send_line_item_cancellation_mail.queue(line_item.id, refund_amount)
    return api_success(result={'cancelled_at': json_date_format(line_item.cancelled_at)},
            doc=_(u"Ticket cancelled"), status_code=200)
Exemplo n.º 9
0
def cancel_line_item(line_item):
    if not line_item.is_cancellable():
        return api_error(message='This ticket is not cancellable',
                         status_code=403,
                         errors=['non cancellable'])

    refund_amount = process_line_item_cancellation(line_item)
    send_line_item_cancellation_mail.delay(line_item.id, refund_amount)
    return api_success(
        result={'cancelled_at': json_date_format(line_item.cancelled_at)},
        doc=_(u"Ticket cancelled"),
        status_code=200)
Exemplo n.º 10
0
def kharcha():
    """
    Accepts JSON containing an array of line_items, with the quantity and item_id set for each line_item.

    Returns JSON of line items in the format:
    {item_id: {'quantity': Y, 'final_amount': Z, 'discounted_amount': Z, 'discount_policy_ids': ['d1', 'd2']}}
    """
    if not request.json or not request.json.get('line_items'):
        return api_error(message='Missing line items',
                    status_code=400)
    line_item_forms = LineItemForm.process_list(request.json.get('line_items'))
    if not line_item_forms:
        return api_error(message='Missing line items',
                    status_code=400)

    # Make line item splits and compute amounts and discounts
    line_items = LineItem.calculate([{'item_id': li_form.data.get('item_id')}
        for li_form in line_item_forms
            for x in range(li_form.data.get('quantity'))], coupons=sanitize_coupons(request.json.get('discount_coupons')))
    items_json = jsonify_line_items(line_items)
    order_final_amount = sum([values['final_amount'] for values in items_json.values() if values['final_amount'] is not None])
    return jsonify(line_items=items_json, order={'final_amount': order_final_amount})
Exemplo n.º 11
0
def edit_invoice_details(order):
    """
    Update invoice with buyer's address and taxid
    """
    if not order.is_confirmed:
        abort(404)
    invoice_dict = request.json.get('invoice')
    if not request.json or not invoice_dict:
        return api_error(message=_(u"Missing invoice details"), status_code=400)

    invoice = Invoice.query.get(request.json.get('invoice_id'))
    if invoice.is_final:
        return api_error(message=_(u"This invoice has been finalised and hence cannot be modified"),
    status_code=400)

    invoice_form = InvoiceForm.from_json(invoice_dict, meta={'csrf': False})
    if not invoice_form.validate():
        return api_error(message=_(u"Incorrect invoice details"),
            status_code=400, errors=invoice_form.errors)
    else:
        invoice_form.populate_obj(invoice)
        db.session.commit()
        return api_success(result={'message': 'Invoice updated', 'invoice': jsonify_invoice(invoice)},
            doc=_(u"Invoice details added"), status_code=201)
Exemplo n.º 12
0
def payment(order):
    """
    Accepts JSON containing `pg_paymentid`.

    Creates a payment object, attempts to 'capture' the payment from Razorpay,
    and returns a JSON containing the result of the operation.

    A successful capture results in a `payment_transaction` registered against the order.
    """
    if not request.json.get('pg_paymentid'):
        return api_error(message="Missing payment id",
                    status_code=400)

    order_amounts = order.get_amounts(LINE_ITEM_STATUS.PURCHASE_ORDER)
    online_payment = OnlinePayment(pg_paymentid=request.json.get('pg_paymentid'), order=order)

    rp_resp = razorpay.capture_payment(online_payment.pg_paymentid, order_amounts.final_amount)
    if rp_resp.status_code == 200:
        online_payment.confirm()
        db.session.add(online_payment)
        # Only INR is supported as of now
        transaction = PaymentTransaction(order=order, online_payment=online_payment, amount=order_amounts.final_amount, currency=CURRENCY.INR)
        db.session.add(transaction)
        order.confirm_sale()
        db.session.add(order)
        invoice_organization = order.organization.invoicer if order.organization.invoicer else order.organization
        invoice = Invoice(order=order, organization=invoice_organization)
        db.session.add(invoice)
        db.session.commit()
        for line_item in order.line_items:
            line_item.confirm()
            db.session.add(line_item)
            if line_item.discount_coupon:
                line_item.discount_coupon.update_used_count()
                db.session.add(line_item.discount_coupon)
        db.session.commit()
        send_receipt_mail.queue(order.id, subject="{item_collection_title}: Thank you for your order (#{invoice_no})!".format(item_collection_title=order.item_collection.title, invoice_no=order.invoice_no))
        return api_success(result={'invoice_id': invoice.id},
            doc=_(u"Payment verified"), status_code=201)
    else:
        online_payment.fail()
        db.session.add(online_payment)
        db.session.commit()
        raise PaymentGatewayError("Online payment failed for order - {order} with the following details - {msg}".format(order=order.id,
            msg=rp_resp.content), 424, 'Your payment failed. Please try again or contact us at {email}.'.format(email=order.organization.contact_email))
Exemplo n.º 13
0
def admin_new_coupon(discount_policy):
    coupon_form = DiscountCouponForm(parent=discount_policy)
    coupons = []
    if not coupon_form.validate_on_submit():
        return api_error(message=_(
            u"The coupon could not be created. Please rectify the indicated issues"
        ),
                         status_code=400,
                         errors=coupon_form.errors)
    if coupon_form.count.data > 1:
        # Create a signed discount coupon code
        for x in range(coupon_form.count.data):
            # No need to store these coupon codes since they are signed
            coupons.append(discount_policy.gen_signed_code())
    else:
        coupon = DiscountCoupon(discount_policy=discount_policy)
        coupon_form.populate_obj(coupon)
        db.session.add(coupon)
        db.session.commit()
        coupons.append(coupon.code)
    return api_success(result={'coupons': coupons},
                       doc=_(u"Discount coupon created"),
                       status_code=201)
Exemplo n.º 14
0
def free(order):
    """
    Completes a order which has a final_amount of 0
    """
    order_amounts = order.get_amounts(LINE_ITEM_STATUS.PURCHASE_ORDER)
    if order_amounts.final_amount == 0:
        order.confirm_sale()
        db.session.add(order)
        db.session.commit()
        for line_item in order.line_items:
            line_item.confirm()
            db.session.add(line_item)
            if line_item.discount_coupon:
                line_item.discount_coupon.update_used_count()
                db.session.add(line_item.discount_coupon)
        db.session.commit()
        send_receipt_mail.queue(order.id, subject="{item_collection_title}: Your registration is confirmed!".format(item_collection_title=order.item_collection.title), template='free_order_confirmation_mail.html.jinja2')
        return api_success(result={'order_id': order.id},
            doc=_(u"Free order confirmed"), status_code=201)

    else:
        return api_error(message="Free order confirmation failed",
            status_code=402)
Exemplo n.º 15
0
def admin_new_discount_policy(organization):
    discount_policy = DiscountPolicy(organization=organization)
    discount_policy_form = DiscountPolicyForm(model=DiscountPolicy)
    discount_policy_form.populate_obj(discount_policy)
    discount_policy_error_msg = _(
        u"The discount could not be created. Please rectify the indicated issues"
    )

    if discount_policy.is_price_based:
        discount_policy_form = PriceBasedDiscountPolicyForm(
            model=DiscountPolicy, parent=discount_policy.organization)
        with db.session.no_autoflush:
            if not discount_policy_form.validate_on_submit():
                return api_error(message=discount_policy_error_msg,
                                 status_code=400,
                                 errors=discount_policy_form.errors)
            discount_policy_form.populate_obj(discount_policy)
            discount_policy.make_name()
            discount_price_form = DiscountPriceForm(model=Price,
                                                    parent=discount_policy)
            if not discount_price_form.validate_on_submit():
                return api_error(message=_(
                    u"There was an issue with the price. Please rectify the indicated issues"
                ),
                                 status_code=400,
                                 errors=discount_price_form.errors)
            discount_price = Price(discount_policy=discount_policy)
            discount_price_form.populate_obj(discount_price)
            discount_price.make_name()
        db.session.add(discount_price)
        discount_policy.items.append(discount_price.item)
    elif discount_policy.is_coupon:
        discount_policy_form = CouponBasedDiscountPolicyForm(
            model=DiscountPolicy, parent=discount_policy.organization)
        with db.session.no_autoflush:
            if not discount_policy_form.validate_on_submit():
                return api_error(message=discount_policy_error_msg,
                                 status_code=400,
                                 errors=discount_policy_form.errors)
        discount_policy_form.populate_obj(discount_policy)
        discount_policy.make_name()
    elif discount_policy.is_automatic:
        discount_policy_form = AutomaticDiscountPolicyForm(
            model=DiscountPolicy, parent=discount_policy.organization)
        with db.session.no_autoflush:
            if not discount_policy_form.validate_on_submit():
                return api_error(message=discount_policy_error_msg,
                                 status_code=400,
                                 errors=discount_policy_form.errors)
        discount_policy_form.populate_obj(discount_policy)
        discount_policy.make_name()
    else:
        return api_error(message=_(u"Incorrect discount type"),
                         status_code=400)

    db.session.add(discount_policy)
    db.session.commit()
    return api_success(
        result={'discount_policy': jsonify_discount_policy(discount_policy)},
        doc=_(u"New discount policy created"),
        status_code=201)
Exemplo n.º 16
0
def order(item_collection):
    """
    Accepts JSON containing an array of line_items with the quantity and item_id
    set for each item, and a buyer hash containing `email`, `fullname` and `phone`.

    Creates a purchase order, and returns a JSON containing the final_amount, order id
    and the URL to be used to register a payment against the order.
    """
    if not request.json or not request.json.get('line_items'):
        return api_error(message='Missing line items', status_code=400)
    line_item_forms = LineItemForm.process_list(request.json.get('line_items'))
    if not line_item_forms:
        return api_error(message='Invalid line items', status_code=400)
    # See comment in LineItemForm about CSRF
    buyer_form = BuyerForm.from_json(request.json.get('buyer'),
                                     meta={'csrf': False})
    if not buyer_form.validate():
        return api_error(message='Invalid buyer details',
                         status_code=400,
                         errors=buyer_form.errors)

    invalid_quantity_error_msg = _(
        u'Selected quantity for ‘{item}’ is not available. Please edit the order and update the quantity'
    )
    item_dicts = Item.get_availability([
        line_item_form.data.get('item_id')
        for line_item_form in line_item_forms
    ])

    for line_item_form in line_item_forms:
        title_quantity_count = item_dicts.get(
            line_item_form.data.get('item_id'))
        if title_quantity_count:
            item_title, item_quantity_total, line_item_count = title_quantity_count
            if (line_item_count +
                    line_item_form.data.get('quantity')) > item_quantity_total:
                return api_error(
                    message=invalid_quantity_error_msg.format(item=item_title),
                    status_code=400,
                    errors=['order calculation error'])
        else:
            item = Item.query.get(line_item_form.data.get('item_id'))
            if line_item_form.data.get('quantity') > item.quantity_total:
                return api_error(
                    message=invalid_quantity_error_msg.format(item=item.title),
                    status_code=400,
                    errors=['order calculation error'])

    user = User.query.filter_by(email=buyer_form.email.data).first()
    order = Order(user=user,
                  organization=item_collection.organization,
                  item_collection=item_collection,
                  buyer_email=buyer_form.email.data,
                  buyer_fullname=buyer_form.fullname.data,
                  buyer_phone=buyer_form.phone.data)

    line_item_tups = LineItem.calculate(
        [{
            'item_id': li_form.data.get('item_id')
        } for li_form in line_item_forms
         for x in range(li_form.data.get('quantity'))],
        coupons=sanitize_coupons(request.json.get('discount_coupons')))

    for idx, line_item_tup in enumerate(line_item_tups):
        item = Item.query.get(line_item_tup.item_id)
        if item.is_available:
            if line_item_tup.discount_policy_id:
                policy = DiscountPolicy.query.get(
                    line_item_tup.discount_policy_id)
            else:
                policy = None
            if line_item_tup.discount_coupon_id:
                coupon = DiscountCoupon.query.get(
                    line_item_tup.discount_coupon_id)
            else:
                coupon = None

            line_item = LineItem(
                order=order,
                item=item,
                discount_policy=policy,
                line_item_seq=idx + 1,
                discount_coupon=coupon,
                ordered_at=datetime.utcnow(),
                base_amount=line_item_tup.base_amount,
                discounted_amount=line_item_tup.discounted_amount,
                final_amount=line_item_tup.base_amount -
                line_item_tup.discounted_amount)
            db.session.add(line_item)
        else:
            return api_error(
                message=_(u'‘{item}’ is no longer available.').format(
                    item=item.title),
                status_code=400,
                errors=['order calculation error'])

    db.session.add(order)

    if request.json.get('order_session'):
        order_session_form = OrderSessionForm.from_json(
            request.json.get('order_session'), meta={'csrf': False})
        if order_session_form.validate():
            order_session = OrderSession(order=order)
            order_session_form.populate_obj(order_session)
            db.session.add(order_session)

    db.session.commit()

    return api_success(doc=_(u"New purchase order created"),
                       result={
                           'order_id':
                           order.id,
                           'order_access_token':
                           order.access_token,
                           'payment_url':
                           url_for('payment', order=order.id),
                           'free_order_url':
                           url_for('free', order=order.id),
                           'final_amount':
                           order.get_amounts(
                               LINE_ITEM_STATUS.PURCHASE_ORDER).final_amount
                       },
                       status_code=201)
Exemplo n.º 17
0
def order(item_collection):
    """
    Accepts JSON containing an array of line_items with the quantity and item_id
    set for each item, and a buyer hash containing `email`, `fullname` and `phone`.

    Creates a purchase order, and returns a JSON containing the final_amount, order id
    and the URL to be used to register a payment against the order.
    """
    if not request.json or not request.json.get('line_items'):
        return api_error(message='Missing line items',
                    status_code=400)
    line_item_forms = LineItemForm.process_list(request.json.get('line_items'))
    if not line_item_forms:
        return api_error(message='Invalid line items',
                    status_code=400)
    # See comment in LineItemForm about CSRF
    buyer_form = BuyerForm.from_json(request.json.get('buyer'), meta={'csrf': False})
    if not buyer_form.validate():
        return api_error(message='Invalid buyer details',
                    status_code=400, errors=buyer_form.errors)

    invalid_quantity_error_msg = _(u'Selected quantity for ‘{item}’ is not available. Please edit the order and update the quantity')
    item_dicts = Item.get_availability([line_item_form.data.get('item_id') for line_item_form in line_item_forms])

    for line_item_form in line_item_forms:
        title_quantity_count = item_dicts.get(line_item_form.data.get('item_id'))
        if title_quantity_count:
            item_title, item_quantity_total, line_item_count = title_quantity_count
            if (line_item_count + line_item_form.data.get('quantity')) > item_quantity_total:
                return api_error(message=invalid_quantity_error_msg.format(item=item_title),
                    status_code=400,
                    errors=['order calculation error'])
        else:
            item = Item.query.get(line_item_form.data.get('item_id'))
            if line_item_form.data.get('quantity') > item.quantity_total:
                return api_error(message=invalid_quantity_error_msg.format(item=item.title),
                    status_code=400,
                    errors=['order calculation error'])

    user = User.query.filter_by(email=buyer_form.email.data).first()
    order = Order(user=user,
        organization=item_collection.organization,
        item_collection=item_collection,
        buyer_email=buyer_form.email.data,
        buyer_fullname=buyer_form.fullname.data,
        buyer_phone=buyer_form.phone.data)

    sanitized_coupon_codes = sanitize_coupons(request.json.get('discount_coupons'))
    line_item_tups = LineItem.calculate([{'item_id': li_form.data.get('item_id')}
        for li_form in line_item_forms
            for x in range(li_form.data.get('quantity'))], coupons=sanitized_coupon_codes)

    for idx, line_item_tup in enumerate(line_item_tups):
        item = Item.query.get(line_item_tup.item_id)

        if item.restricted_entry:
            if not sanitized_coupon_codes or not DiscountPolicy.is_valid_access_coupon(item, sanitized_coupon_codes):
                # Skip adding a restricted item to the cart without the proper access code
                break

        if item.is_available:
            if line_item_tup.discount_policy_id:
                policy = DiscountPolicy.query.get(line_item_tup.discount_policy_id)
            else:
                policy = None
            if line_item_tup.discount_coupon_id:
                coupon = DiscountCoupon.query.get(line_item_tup.discount_coupon_id)
            else:
                coupon = None

            line_item = LineItem(order=order, item=item, discount_policy=policy,
                line_item_seq=idx+1,
                discount_coupon=coupon,
                ordered_at=datetime.utcnow(),
                base_amount=line_item_tup.base_amount,
                discounted_amount=line_item_tup.discounted_amount,
                final_amount=line_item_tup.base_amount-line_item_tup.discounted_amount)
            db.session.add(line_item)
        else:
            return api_error(message=_(u'‘{item}’ is no longer available.').format(item=item.title),
                    status_code=400,
                    errors=['order calculation error'])

    db.session.add(order)

    if request.json.get('order_session'):
        order_session_form = OrderSessionForm.from_json(request.json.get('order_session'), meta={'csrf': False})
        if order_session_form.validate():
            order_session = OrderSession(order=order)
            order_session_form.populate_obj(order_session)
            db.session.add(order_session)

    db.session.commit()

    return api_success(doc=_(u"New purchase order created"),
        result={'order_id': order.id,
        'order_access_token': order.access_token,
        'payment_url': url_for('payment', order=order.id),
        'free_order_url': url_for('free', order=order.id),
        'final_amount': order.get_amounts(LINE_ITEM_STATUS.PURCHASE_ORDER).final_amount},
        status_code=201)