namespace GoodBooks.PAY.PAYDAL.Query.Loyalty
{
///
/// APPEND-ONLY LEDGER — This file must contain only INSERT and SELECT constants.
/// No UPDATE or DELETE SQL is permitted. Compensate with new negative-points INSERT rows.
/// Violation breaks the audit trail and loyalty balance integrity.
///
/// Required indexes:
/// TLOYALTYLEDGER: (ENROLLMENTID, TENANTID, EXPIRYDATE) — covers available-points SUM
/// TLOYALTYLEDGER: (ENROLLMENTID, CREATEDON DESC) — covers paged ledger history
/// TLOYALTYLEDGER: (TENANTID, ENTRYTYPE, EXPIRYDATE) — covers expiry-job polling
///
public static class LoyaltyLedgerQB
{
///
/// Inserts a single ledger entry.
/// LOYALTYLEDGERID is an app-generated INT PK — the caller must supply it
/// (e.g. from an AutoNumber service). Include it in the INSERT column list.
/// CREATEDON is set server-side to GETUTCDATE() to eliminate clock-skew risk.
///
public const string INSERT_LEDGER_ENTRY = @"
INSERT INTO TLOYALTYLEDGER
(
LOYALTYLEDGERID,
ENROLLMENTID,
LOYALTYPROGRAMID,
CUSTOMERID,
ENTRYTYPE,
POINTS,
REFERENCEORDERID,
EARNCFGID,
EXPIRYDATE,
REMARKS,
CREATEDBYID,
CREATEDON,
TENANTID
)
VALUES
(
@LoyaltyLedgerId,
@EnrollmentId,
@LoyaltyProgramId,
@CustomerId,
@EntryType,
@Points,
@ReferenceOrderId,
@EarnCfgId,
@ExpiryDate,
@Remarks,
@CreatedById,
GETUTCDATE(),
@TenantId
)";
///
/// Returns the live balance of non-expired points for an enrollment.
/// COALESCE ensures 0 is returned when no rows match (new enrollment).
/// SUM includes both positive (earn) and negative (redeem/expire/reverse) rows.
/// Only rows where EXPIRYDATE IS NULL or EXPIRYDATE > GETDATE() are included.
///
public const string GET_AVAILABLE_POINTS = @"
SELECT COALESCE(SUM(POINTS), 0) AS AvailablePoints
FROM TLOYALTYLEDGER
WHERE ENROLLMENTID = @EnrollmentId
AND TENANTID = @TenantId
AND (EXPIRYDATE IS NULL OR EXPIRYDATE > GETDATE())";
///
/// Paged ledger history for an enrollment, newest entries first.
/// JOINs TPAYORDER to surface the PayOrderNo for display.
/// OFFSET/FETCH paging — caller supplies @Offset and @PageSize.
///
public const string GET_LEDGER_PAGED = @"
SELECT L.LOYALTYLEDGERID AS LoyaltyLedgerId,
L.ENROLLMENTID AS EnrollmentId,
L.LOYALTYPROGRAMID AS LoyaltyProgramId,
L.CUSTOMERID AS CustomerId,
L.ENTRYTYPE AS EntryType,
L.POINTS AS Points,
L.REFERENCEORDERID AS ReferenceOrderId,
L.EARNCFGID AS EarnCfgId,
L.EXPIRYDATE AS ExpiryDate,
L.REMARKS AS Remarks,
L.CREATEDBYID AS CreatedById,
L.CREATEDON AS CreatedOn,
L.TENANTID AS TenantId,
PO.PAYORDERNO AS PayOrderNo
FROM TLOYALTYLEDGER L
LEFT JOIN TPAYORDER PO ON PO.PAYORDERID = L.REFERENCEORDERID
WHERE L.ENROLLMENTID = @EnrollmentId
AND L.TENANTID = @TenantId
ORDER BY L.CREATEDON DESC
OFFSET @Offset ROWS
FETCH NEXT @PageSize ROWS ONLY";
///
/// COUNT companion for GET_LEDGER_PAGED — used by the DAL to return total records.
///
public const string GET_LEDGER_COUNT = @"
SELECT COUNT(1)
FROM TLOYALTYLEDGER
WHERE ENROLLMENTID = @EnrollmentId
AND TENANTID = @TenantId";
///
/// Identifies enrollments that have points expiring today (for the daily expiry job).
/// Sums only Earn-type rows (ENTRYTYPE=1) scheduled to expire today so the job
/// knows how many compensating negative rows to insert.
/// Groups by ENROLLMENTID so one job iteration processes one enrollment at a time.
///
public const string GET_EXPIRING_POINTS = @"
SELECT ENROLLMENTID AS EnrollmentId,
SUM(POINTS) AS PointsExpiring
FROM TLOYALTYLEDGER
WHERE CAST(EXPIRYDATE AS DATE) = CAST(GETDATE() AS DATE)
AND ENTRYTYPE = 1
AND TENANTID = @TenantId
GROUP BY ENROLLMENTID";
}
}