Auto-apply user balance on cash and card orders
Balance from cash change now silently reduces the amount owed on the next order. For cash: deducted immediately in submitCash, reduces cash the worker needs to collect (or skips cash task entirely if fully covered). For card: reduces the Stripe PaymentIntent amount, deducted in webhook on successful payment. Receipt shows "Balance applied" line. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
96c2ed3fc1
commit
c580e6ec78
6 changed files with 143 additions and 19 deletions
|
|
@ -64,7 +64,8 @@ if (cgi.REQUEST_METHOD == "GET") {
|
||||||
LastName,
|
LastName,
|
||||||
EmailAddress,
|
EmailAddress,
|
||||||
ContactNumber,
|
ContactNumber,
|
||||||
ImageExtension
|
ImageExtension,
|
||||||
|
Balance
|
||||||
FROM Users
|
FROM Users
|
||||||
WHERE ID = :userId
|
WHERE ID = :userId
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
|
|
@ -88,7 +89,8 @@ if (cgi.REQUEST_METHOD == "GET") {
|
||||||
"LastName": qUser.LastName ?: "",
|
"LastName": qUser.LastName ?: "",
|
||||||
"Email": qUser.EmailAddress ?: "",
|
"Email": qUser.EmailAddress ?: "",
|
||||||
"Phone": qUser.ContactNumber ?: "",
|
"Phone": qUser.ContactNumber ?: "",
|
||||||
"AvatarUrl": avatarUrl
|
"AvatarUrl": avatarUrl,
|
||||||
|
"Balance": val(qUser.Balance)
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
abort;
|
abort;
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,8 @@
|
||||||
<!--- Get order details with business fee rate --->
|
<!--- Get order details with business fee rate --->
|
||||||
<cfset qOrder = queryExecute(
|
<cfset qOrder = queryExecute(
|
||||||
"SELECT o.ID, o.StatusID, o.UserID, o.BusinessID, o.ServicePointID, o.PaymentID,
|
"SELECT o.ID, o.StatusID, o.UserID, o.BusinessID, o.ServicePointID, o.PaymentID,
|
||||||
b.PayfritFee
|
o.DeliveryFee, o.OrderTypeID,
|
||||||
|
b.PayfritFee, b.TaxRate
|
||||||
FROM Orders o
|
FROM Orders o
|
||||||
INNER JOIN Businesses b ON b.ID = o.BusinessID
|
INNER JOIN Businesses b ON b.ID = o.BusinessID
|
||||||
WHERE o.ID = ? LIMIT 1",
|
WHERE o.ID = ? LIMIT 1",
|
||||||
|
|
@ -74,20 +75,58 @@
|
||||||
<cfset subtotal = val(qSubtotal.Subtotal)>
|
<cfset subtotal = val(qSubtotal.Subtotal)>
|
||||||
<cfset platformFee = subtotal * feeRate>
|
<cfset platformFee = subtotal * feeRate>
|
||||||
|
|
||||||
<!--- Create Payment record with expected cash amount --->
|
<!--- Calculate full order total for balance application --->
|
||||||
<cfset CashAmountCents = round(CashAmount * 100)>
|
<cfset taxRate = (isNumeric(qOrder.TaxRate) AND val(qOrder.TaxRate) GT 0) ? val(qOrder.TaxRate) : 0>
|
||||||
|
<cfset taxAmount = subtotal * taxRate>
|
||||||
|
<cfset deliveryFee = (val(qOrder.OrderTypeID) EQ 3) ? val(qOrder.DeliveryFee) : 0>
|
||||||
|
<cfset orderTotal = subtotal + taxAmount + platformFee + Tip + deliveryFee>
|
||||||
|
|
||||||
|
<!--- Auto-apply user balance (silently reduces cash owed) --->
|
||||||
|
<cfset balanceApplied = 0>
|
||||||
|
<cfset cashNeeded = orderTotal>
|
||||||
|
<cfif val(qOrder.UserID) GT 0>
|
||||||
|
<cfset qBalance = queryExecute(
|
||||||
|
"SELECT Balance FROM Users WHERE ID = ?",
|
||||||
|
[ { value = qOrder.UserID, cfsqltype = "cf_sql_integer" } ],
|
||||||
|
{ datasource = "payfrit" }
|
||||||
|
)>
|
||||||
|
<cfset userBalance = val(qBalance.Balance)>
|
||||||
|
<cfif userBalance GT 0>
|
||||||
|
<cfset balanceToApply = min(userBalance, orderTotal)>
|
||||||
|
<!--- Atomic deduct: only succeeds if balance is still sufficient --->
|
||||||
|
<cfset qDeduct = queryExecute(
|
||||||
|
"UPDATE Users SET Balance = Balance - ? WHERE ID = ? AND Balance >= ?",
|
||||||
|
[
|
||||||
|
{ value = balanceToApply, cfsqltype = "cf_sql_decimal" },
|
||||||
|
{ value = qOrder.UserID, cfsqltype = "cf_sql_integer" },
|
||||||
|
{ value = balanceToApply, cfsqltype = "cf_sql_decimal" }
|
||||||
|
],
|
||||||
|
{ datasource = "payfrit", result = "deductResult" }
|
||||||
|
)>
|
||||||
|
<cfif val(deductResult.recordCount) GT 0>
|
||||||
|
<cfset balanceApplied = balanceToApply>
|
||||||
|
<cfset cashNeeded = orderTotal - balanceApplied>
|
||||||
|
<cfif cashNeeded LT 0><cfset cashNeeded = 0></cfif>
|
||||||
|
</cfif>
|
||||||
|
</cfif>
|
||||||
|
</cfif>
|
||||||
|
|
||||||
|
<!--- Create Payment record with adjusted cash amount --->
|
||||||
|
<cfset CashAmountCents = round(cashNeeded * 100)>
|
||||||
<cfset qInsertPayment = queryExecute(
|
<cfset qInsertPayment = queryExecute(
|
||||||
"INSERT INTO Payments (
|
"INSERT INTO Payments (
|
||||||
PaymentPaidInCash,
|
PaymentPaidInCash,
|
||||||
|
PaymentFromPayfritBalance,
|
||||||
PaymentFromCreditCard,
|
PaymentFromCreditCard,
|
||||||
PaymentSentByUserID,
|
PaymentSentByUserID,
|
||||||
PaymentReceivedByUserID,
|
PaymentReceivedByUserID,
|
||||||
PaymentOrderID,
|
PaymentOrderID,
|
||||||
PaymentPayfritsCut,
|
PaymentPayfritsCut,
|
||||||
PaymentAddedOn
|
PaymentAddedOn
|
||||||
) VALUES (?, 0, ?, 0, ?, ?, NOW())",
|
) VALUES (?, ?, 0, ?, 0, ?, ?, NOW())",
|
||||||
[
|
[
|
||||||
{ value = CashAmount, cfsqltype = "cf_sql_decimal" },
|
{ value = cashNeeded, cfsqltype = "cf_sql_decimal" },
|
||||||
|
{ value = balanceApplied, cfsqltype = "cf_sql_decimal" },
|
||||||
{ value = qOrder.UserID, cfsqltype = "cf_sql_integer" },
|
{ value = qOrder.UserID, cfsqltype = "cf_sql_integer" },
|
||||||
{ value = OrderID, cfsqltype = "cf_sql_integer" },
|
{ value = OrderID, cfsqltype = "cf_sql_integer" },
|
||||||
{ value = platformFee, cfsqltype = "cf_sql_decimal" }
|
{ value = platformFee, cfsqltype = "cf_sql_decimal" }
|
||||||
|
|
@ -97,27 +136,41 @@
|
||||||
|
|
||||||
<cfset PaymentID = insertResult.generatedKey>
|
<cfset PaymentID = insertResult.generatedKey>
|
||||||
|
|
||||||
<!--- Update order: link payment, set status to submitted, store platform fee --->
|
<!--- Update order: link payment, set status to submitted, store platform fee and balance --->
|
||||||
|
<!--- If balance covers full order, mark as paid immediately (no cash task needed) --->
|
||||||
|
<cfset fullyPaidByBalance = (cashNeeded LTE 0)>
|
||||||
|
<cfset paymentStatus = fullyPaidByBalance ? "paid" : "pending">
|
||||||
<cfset queryExecute(
|
<cfset queryExecute(
|
||||||
"UPDATE Orders
|
"UPDATE Orders
|
||||||
SET StatusID = 1,
|
SET StatusID = 1,
|
||||||
PaymentID = ?,
|
PaymentID = ?,
|
||||||
PaymentStatus = 'pending',
|
PaymentStatus = ?,
|
||||||
PlatformFee = ?,
|
PlatformFee = ?,
|
||||||
TipAmount = ?,
|
TipAmount = ?,
|
||||||
|
BalanceApplied = ?,
|
||||||
SubmittedOn = NOW(),
|
SubmittedOn = NOW(),
|
||||||
LastEditedOn = NOW()
|
LastEditedOn = NOW(),
|
||||||
|
PaymentCompletedOn = CASE WHEN ? = 1 THEN NOW() ELSE PaymentCompletedOn END
|
||||||
WHERE ID = ?",
|
WHERE ID = ?",
|
||||||
[
|
[
|
||||||
{ value = PaymentID, cfsqltype = "cf_sql_integer" },
|
{ value = PaymentID, cfsqltype = "cf_sql_integer" },
|
||||||
|
{ value = paymentStatus, cfsqltype = "cf_sql_varchar" },
|
||||||
{ value = platformFee, cfsqltype = "cf_sql_decimal" },
|
{ value = platformFee, cfsqltype = "cf_sql_decimal" },
|
||||||
{ value = Tip, cfsqltype = "cf_sql_decimal" },
|
{ value = Tip, cfsqltype = "cf_sql_decimal" },
|
||||||
|
{ value = balanceApplied, cfsqltype = "cf_sql_decimal" },
|
||||||
|
{ value = fullyPaidByBalance ? 1 : 0, cfsqltype = "cf_sql_integer" },
|
||||||
{ value = OrderID, cfsqltype = "cf_sql_integer" }
|
{ value = OrderID, cfsqltype = "cf_sql_integer" }
|
||||||
],
|
],
|
||||||
{ datasource = "payfrit" }
|
{ datasource = "payfrit" }
|
||||||
)>
|
)>
|
||||||
|
|
||||||
<cfset apiAbort({ "OK": true, "OrderID": OrderID, "PaymentID": PaymentID, "CashAmountCents": CashAmountCents, "MESSAGE": "Order submitted with cash payment." })>
|
<cfset response = { "OK": true, "OrderID": OrderID, "PaymentID": PaymentID, "CashAmountCents": CashAmountCents, "MESSAGE": "Order submitted with cash payment." }>
|
||||||
|
<cfif balanceApplied GT 0>
|
||||||
|
<cfset response["BalanceApplied"] = round(balanceApplied * 100) / 100>
|
||||||
|
<cfset response["BalanceAppliedCents"] = round(balanceApplied * 100)>
|
||||||
|
<cfset response["FullyPaidByBalance"] = (cashNeeded LTE 0)>
|
||||||
|
</cfif>
|
||||||
|
<cfset apiAbort(response)>
|
||||||
|
|
||||||
<cfcatch>
|
<cfcatch>
|
||||||
<cfset apiAbort({
|
<cfset apiAbort({
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,8 @@ try {
|
||||||
qOrder = queryExecute("
|
qOrder = queryExecute("
|
||||||
SELECT o.DeliveryFee, o.OrderTypeID, o.GrantID, o.GrantOwnerBusinessID,
|
SELECT o.DeliveryFee, o.OrderTypeID, o.GrantID, o.GrantOwnerBusinessID,
|
||||||
o.GrantEconomicsType, o.GrantEconomicsValue, o.StripePaymentIntentID,
|
o.GrantEconomicsType, o.GrantEconomicsValue, o.StripePaymentIntentID,
|
||||||
o.UserID, u.StripeCustomerId, u.EmailAddress, u.FirstName, u.LastName
|
o.UserID, o.BalanceApplied AS PrevBalanceApplied,
|
||||||
|
u.StripeCustomerId, u.EmailAddress, u.FirstName, u.LastName, u.Balance
|
||||||
FROM Orders o
|
FROM Orders o
|
||||||
LEFT JOIN Users u ON u.ID = o.UserID
|
LEFT JOIN Users u ON u.ID = o.UserID
|
||||||
WHERE o.ID = :orderID
|
WHERE o.ID = :orderID
|
||||||
|
|
@ -126,10 +127,36 @@ try {
|
||||||
payfritBusinessFee = subtotal * businessFeePercent;
|
payfritBusinessFee = subtotal * businessFeePercent;
|
||||||
totalBeforeCardFee = subtotal + tax + tip + deliveryFee + payfritCustomerFee;
|
totalBeforeCardFee = subtotal + tax + tip + deliveryFee + payfritCustomerFee;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// AUTO-APPLY USER BALANCE (silently reduces card charge)
|
||||||
|
// ============================================================
|
||||||
|
balanceApplied = 0;
|
||||||
|
userBalance = val(qOrder.Balance ?: 0);
|
||||||
|
orderUserID = val(qOrder.UserID ?: 0);
|
||||||
|
if (userBalance > 0 && orderUserID > 0) {
|
||||||
|
balanceApplied = min(userBalance, totalBeforeCardFee);
|
||||||
|
// Ensure Stripe minimum: adjusted amount after card fee must be >= $0.50
|
||||||
|
adjustedTest = ((totalBeforeCardFee - balanceApplied) + cardFeeFixed) / (1 - cardFeePercent);
|
||||||
|
if (adjustedTest < 0.50) {
|
||||||
|
// Cap balance so Stripe charge stays >= $0.50
|
||||||
|
maxBalance = totalBeforeCardFee - ((0.50 * (1 - cardFeePercent)) - cardFeeFixed);
|
||||||
|
balanceApplied = max(0, min(userBalance, maxBalance));
|
||||||
|
}
|
||||||
|
// Store intent on order (actual deduction happens in webhook after payment succeeds)
|
||||||
|
if (balanceApplied > 0) {
|
||||||
|
queryExecute("UPDATE Orders SET BalanceApplied = :bal WHERE ID = :orderID",
|
||||||
|
{ bal: { value: balanceApplied, cfsqltype: "cf_sql_decimal" }, orderID: orderID },
|
||||||
|
{ datasource: "payfrit" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply balance: reduce the pre-card-fee amount, recalculate card fee on smaller amount
|
||||||
|
adjustedBeforeCardFee = totalBeforeCardFee - balanceApplied;
|
||||||
|
|
||||||
// Stripe charges 2.9% + $0.30 on the TOTAL charged, not the pre-fee amount.
|
// Stripe charges 2.9% + $0.30 on the TOTAL charged, not the pre-fee amount.
|
||||||
// To fully pass through Stripe fees: charge = (net + fixed) / (1 - percent)
|
// To fully pass through Stripe fees: charge = (net + fixed) / (1 - percent)
|
||||||
totalCustomerPays = (totalBeforeCardFee + cardFeeFixed) / (1 - cardFeePercent);
|
totalCustomerPays = (adjustedBeforeCardFee + cardFeeFixed) / (1 - cardFeePercent);
|
||||||
cardFee = totalCustomerPays - totalBeforeCardFee;
|
cardFee = totalCustomerPays - adjustedBeforeCardFee;
|
||||||
|
|
||||||
// Convert to cents for Stripe
|
// Convert to cents for Stripe
|
||||||
totalAmountCents = round(totalCustomerPays * 100);
|
totalAmountCents = round(totalCustomerPays * 100);
|
||||||
|
|
@ -171,6 +198,14 @@ try {
|
||||||
response["PAYMENT_INTENT_ID"] = existingPi.id;
|
response["PAYMENT_INTENT_ID"] = existingPi.id;
|
||||||
response["PUBLISHABLE_KEY"] = application.stripePublishableKey ?: "pk_test_sPBNzSyJ9HcEPJGC7dSo8NqN";
|
response["PUBLISHABLE_KEY"] = application.stripePublishableKey ?: "pk_test_sPBNzSyJ9HcEPJGC7dSo8NqN";
|
||||||
response["REUSED"] = true;
|
response["REUSED"] = true;
|
||||||
|
response["FEE_BREAKDOWN"] = {
|
||||||
|
"SUBTOTAL": subtotal, "TAX": tax, "TIP": tip,
|
||||||
|
"DELIVERY_FEE": deliveryFee, "PAYFRIT_FEE": payfritCustomerFee,
|
||||||
|
"CARD_FEE": cardFee, "BALANCE_APPLIED": balanceApplied,
|
||||||
|
"TOTAL": totalCustomerPays,
|
||||||
|
"TOTAL_BEFORE_BALANCE": (balanceApplied > 0) ? totalCustomerPays + balanceApplied : 0,
|
||||||
|
"GRANT_OWNER_FEE_CENTS": grantOwnerFeeCents
|
||||||
|
};
|
||||||
writeOutput(serializeJSON(response));
|
writeOutput(serializeJSON(response));
|
||||||
abort;
|
abort;
|
||||||
} else if (piStatus == "succeeded") {
|
} else if (piStatus == "succeeded") {
|
||||||
|
|
@ -360,7 +395,9 @@ try {
|
||||||
"DELIVERY_FEE": deliveryFee,
|
"DELIVERY_FEE": deliveryFee,
|
||||||
"PAYFRIT_FEE": payfritCustomerFee,
|
"PAYFRIT_FEE": payfritCustomerFee,
|
||||||
"CARD_FEE": cardFee,
|
"CARD_FEE": cardFee,
|
||||||
|
"BALANCE_APPLIED": balanceApplied,
|
||||||
"TOTAL": totalCustomerPays,
|
"TOTAL": totalCustomerPays,
|
||||||
|
"TOTAL_BEFORE_BALANCE": (balanceApplied > 0) ? totalCustomerPays + balanceApplied : 0,
|
||||||
"GRANT_OWNER_FEE_CENTS": grantOwnerFeeCents
|
"GRANT_OWNER_FEE_CENTS": grantOwnerFeeCents
|
||||||
};
|
};
|
||||||
response["STRIPE_CONNECT_ENABLED"] = hasStripeConnect;
|
response["STRIPE_CONNECT_ENABLED"] = hasStripeConnect;
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,17 @@ try {
|
||||||
", { orderID: orderID });
|
", { orderID: orderID });
|
||||||
|
|
||||||
writeLog(file="stripe_webhooks", text="Order #orderID# marked as paid");
|
writeLog(file="stripe_webhooks", text="Order #orderID# marked as paid");
|
||||||
|
|
||||||
|
// === DEDUCT USER BALANCE (if balance was applied at checkout) ===
|
||||||
|
qBalOrder = queryTimed("
|
||||||
|
SELECT BalanceApplied, UserID FROM Orders WHERE ID = :orderID
|
||||||
|
", { orderID: orderID });
|
||||||
|
if (qBalOrder.recordCount > 0 && val(qBalOrder.BalanceApplied) > 0) {
|
||||||
|
queryTimed("
|
||||||
|
UPDATE Users SET Balance = GREATEST(Balance - :amount, 0) WHERE ID = :userID
|
||||||
|
", { amount: val(qBalOrder.BalanceApplied), userID: val(qBalOrder.UserID) });
|
||||||
|
writeLog(file="stripe_webhooks", text="Order #orderID# balance deducted: $#qBalOrder.BalanceApplied#");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// === WORKER PAYOUT TRANSFER ===
|
// === WORKER PAYOUT TRANSFER ===
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,8 @@
|
||||||
</cfif>
|
</cfif>
|
||||||
|
|
||||||
<cfset qOrderTotal = queryTimed("
|
<cfset qOrderTotal = queryTimed("
|
||||||
SELECT SUM(oli.Price * oli.Quantity) AS Subtotal, o.TipAmount, o.DeliveryFee, o.OrderTypeID, b.TaxRate, b.PayfritFee
|
SELECT SUM(oli.Price * oli.Quantity) AS Subtotal, o.TipAmount, o.DeliveryFee, o.OrderTypeID,
|
||||||
|
o.BalanceApplied, b.TaxRate, b.PayfritFee
|
||||||
FROM Orders o
|
FROM Orders o
|
||||||
INNER JOIN Businesses b ON b.ID = o.BusinessID
|
INNER JOIN Businesses b ON b.ID = o.BusinessID
|
||||||
LEFT JOIN OrderLineItems oli ON oli.OrderID = o.ID AND oli.IsDeleted = b'0'
|
LEFT JOIN OrderLineItems oli ON oli.OrderID = o.ID AND oli.IsDeleted = b'0'
|
||||||
|
|
@ -171,16 +172,23 @@
|
||||||
|
|
||||||
<cfset orderTotalCents = round((cashSubtotal + cashTax + cashTip + cashDeliveryFee + customerFeeDollars) * 100)>
|
<cfset orderTotalCents = round((cashSubtotal + cashTax + cashTip + cashDeliveryFee + customerFeeDollars) * 100)>
|
||||||
|
|
||||||
<cfif CashReceivedCents LT orderTotalCents>
|
<!--- Account for balance already applied in submitCash.cfm --->
|
||||||
<cfset apiAbort({ "OK": false, "ERROR": "insufficient_cash", "MESSAGE": "Cash received ($#numberFormat(CashReceivedCents/100, '0.00')#) is less than order total ($#numberFormat(orderTotalCents/100, '0.00')#)." })>
|
<cfset balanceAppliedCents = round(val(qOrderTotal.BalanceApplied) * 100)>
|
||||||
|
<cfset cashOwedCents = orderTotalCents - balanceAppliedCents>
|
||||||
|
<cfif cashOwedCents LT 0><cfset cashOwedCents = 0></cfif>
|
||||||
|
|
||||||
|
<cfif CashReceivedCents LT cashOwedCents>
|
||||||
|
<cfset apiAbort({ "OK": false, "ERROR": "insufficient_cash", "MESSAGE": "Cash received ($#numberFormat(CashReceivedCents/100, '0.00')#) is less than cash owed ($#numberFormat(cashOwedCents/100, '0.00')#)." })>
|
||||||
</cfif>
|
</cfif>
|
||||||
|
|
||||||
<cfset payfritRevenueCents = round(payfritRevenueDollars * 100)>
|
<cfset payfritRevenueCents = round(payfritRevenueDollars * 100)>
|
||||||
<cfset changeCents = CashReceivedCents - orderTotalCents>
|
<cfset changeCents = CashReceivedCents - cashOwedCents>
|
||||||
<cfset businessReceivesCents = orderTotalCents - payfritRevenueCents>
|
<cfset businessReceivesCents = orderTotalCents - payfritRevenueCents>
|
||||||
|
|
||||||
<cfset cashResult = {
|
<cfset cashResult = {
|
||||||
"orderTotalCents": orderTotalCents,
|
"orderTotalCents": orderTotalCents,
|
||||||
|
"cashOwedCents": cashOwedCents,
|
||||||
|
"balanceAppliedCents": balanceAppliedCents,
|
||||||
"cashReceivedCents": CashReceivedCents,
|
"cashReceivedCents": CashReceivedCents,
|
||||||
"changeCents": changeCents,
|
"changeCents": changeCents,
|
||||||
"customerFeeCents": round(customerFeeDollars * 100),
|
"customerFeeCents": round(customerFeeDollars * 100),
|
||||||
|
|
@ -418,6 +426,10 @@
|
||||||
<cfset response["BusinessFee"] = numberFormat(round(businessFeeDollars * 100) / 100, "0.00")>
|
<cfset response["BusinessFee"] = numberFormat(round(businessFeeDollars * 100) / 100, "0.00")>
|
||||||
<cfset response["PayfritRevenue"] = numberFormat(payfritRevenueCents / 100, "0.00")>
|
<cfset response["PayfritRevenue"] = numberFormat(payfritRevenueCents / 100, "0.00")>
|
||||||
<cfset response["BusinessReceives"] = numberFormat(businessReceivesCents / 100, "0.00")>
|
<cfset response["BusinessReceives"] = numberFormat(businessReceivesCents / 100, "0.00")>
|
||||||
|
<cfif balanceAppliedCents GT 0>
|
||||||
|
<cfset response["BalanceApplied"] = numberFormat(balanceAppliedCents / 100, "0.00")>
|
||||||
|
<cfset response["CashOwed"] = numberFormat(cashOwedCents / 100, "0.00")>
|
||||||
|
</cfif>
|
||||||
</cfif>
|
</cfif>
|
||||||
|
|
||||||
<cfset apiAbort(response)>
|
<cfset apiAbort(response)>
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
<cfset cart_grand_total = 0>
|
<cfset cart_grand_total = 0>
|
||||||
|
|
||||||
<cfquery name="get_order_info">
|
<cfquery name="get_order_info">
|
||||||
SELECT O.OrderTypeID, O.BusinessID, O.Remarks, O.ID,
|
SELECT O.OrderTypeID, O.BusinessID, O.Remarks, O.ID, O.BalanceApplied,
|
||||||
B.Name, B.TaxRate, B.PayfritFee
|
B.Name, B.TaxRate, B.PayfritFee
|
||||||
FROM Orders O
|
FROM Orders O
|
||||||
JOIN Businesses B ON B.ID = O.BusinessID
|
JOIN Businesses B ON B.ID = O.BusinessID
|
||||||
|
|
@ -347,6 +347,15 @@
|
||||||
<span>#dollarFormat(cardFee)#</span>
|
<span>#dollarFormat(cardFee)#</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<cfset receiptBalanceApplied = val(get_order_info.BalanceApplied)>
|
||||||
|
<cfif receiptBalanceApplied GT 0>
|
||||||
|
<div class="total-row">
|
||||||
|
<span>Balance applied</span>
|
||||||
|
<span>-#dollarFormat(receiptBalanceApplied)#</span>
|
||||||
|
</div>
|
||||||
|
<cfset order_grand_total = order_grand_total - receiptBalanceApplied>
|
||||||
|
</cfif>
|
||||||
|
|
||||||
<div class="total-row grand-total">
|
<div class="total-row grand-total">
|
||||||
<span>Total</span>
|
<span>Total</span>
|
||||||
<span>#dollarFormat(order_grand_total)#</span>
|
<span>#dollarFormat(order_grand_total)#</span>
|
||||||
|
|
|
||||||
Reference in a new issue