feat(MED-97): update cart flow for using benefits

This commit is contained in:
2025-09-26 13:24:09 +03:00
parent 56f84a003c
commit db38e602aa
15 changed files with 419 additions and 81 deletions

View File

@@ -12,6 +12,7 @@
"./personal-account-settings": "./src/components/personal-account-settings/index.ts",
"./components": "./src/components/index.ts",
"./hooks/*": "./src/hooks/*.ts",
"./services/*": "./src/server/services/*.ts",
"./api": "./src/server/api.ts",
"./types/*": "./src/types/*.ts"
},

View File

@@ -0,0 +1,125 @@
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import type { AccountBalanceEntry } from '../../types/account-balance-entry';
export type AccountBalanceSummary = {
totalBalance: number;
expiringSoon: number;
recentEntries: AccountBalanceEntry[];
};
export class AccountBalanceService {
private supabase: ReturnType<typeof getSupabaseServerClient>;
constructor() {
this.supabase = getSupabaseServerClient();
}
/**
* Get the current balance for a specific account
*/
async getAccountBalance(accountId: string): Promise<number> {
const { data, error } = await this.supabase
.schema('medreport')
.rpc('get_account_balance', {
p_account_id: accountId,
});
if (error) {
console.error('Error getting account balance:', error);
throw new Error('Failed to get account balance');
}
return data || 0;
}
/**
* Get balance entries for an account with pagination
*/
async getAccountBalanceEntries(
accountId: string,
options: {
limit?: number;
offset?: number;
entryType?: string;
includeInactive?: boolean;
} = {}
): Promise<{
entries: AccountBalanceEntry[];
total: number;
}> {
const { limit = 50, offset = 0, entryType, includeInactive = false } = options;
let query = this.supabase
.schema('medreport')
.from('account_balance_entries')
.select('*', { count: 'exact' })
.eq('account_id', accountId)
.order('created_at', { ascending: false })
.range(offset, offset + limit - 1);
if (!includeInactive) {
query = query.eq('is_active', true);
}
if (entryType) {
query = query.eq('entry_type', entryType);
}
const { data, error, count } = await query;
if (error) {
console.error('Error getting account balance entries:', error);
throw new Error('Failed to get account balance entries');
}
return {
entries: data || [],
total: count || 0,
};
}
/**
* Get balance summary for dashboard display
*/
async getBalanceSummary(accountId: string): Promise<AccountBalanceSummary> {
const [balance, entries] = await Promise.all([
this.getAccountBalance(accountId),
this.getAccountBalanceEntries(accountId, { limit: 5 }),
]);
// Calculate expiring balance (next 30 days)
const thirtyDaysFromNow = new Date();
thirtyDaysFromNow.setDate(thirtyDaysFromNow.getDate() + 30);
const { data: expiringData, error: expiringError } = await this.supabase
.schema('medreport')
.from('account_balance_entries')
.select('amount')
.eq('account_id', accountId)
.eq('is_active', true)
.not('expires_at', 'is', null)
.lte('expires_at', thirtyDaysFromNow.toISOString());
if (expiringError) {
console.error('Error getting expiring balance:', expiringError);
}
const expiringSoon = expiringData?.reduce((sum, entry) => sum + (entry.amount || 0), 0) || 0;
return {
totalBalance: balance,
expiringSoon,
recentEntries: entries.entries,
};
}
async processPeriodicBenefitDistributions(): Promise<void> {
const { error } = await this.supabase.schema('medreport').rpc('process_periodic_benefit_distributions')
if (error) {
console.error('Error processing periodic benefit distributions:', error);
throw new Error('Failed to process periodic benefit distributions');
}
}
}

View File

@@ -0,0 +1,3 @@
import type { Database } from '@kit/supabase/database';
export type AccountBalanceEntry = Database['medreport']['Tables']['account_balance_entries']['Row'];

View File

@@ -253,6 +253,37 @@ export async function setShippingMethod({
.catch(medusaError);
}
export async function initiateMultiPaymentSession(
cart: HttpTypes.StoreCart,
benefitsAmount: number,
) {
const headers = {
...(await getAuthHeaders()),
};
return sdk.client.fetch<unknown>(`/store/multi-payment`, {
method: 'POST',
body: { cartId: cart.id, benefitsAmount },
headers,
})
.then(async (response) => {
console.info('Payment session initiated:', response);
const cartCacheTag = await getCacheTag('carts');
revalidateTag(cartCacheTag);
return response as {
montonioPaymentSessionId: string | null;
companyBenefitsPaymentSessionId: string | null;
totalByBenefits: number;
totalByMontonio: number;
isFullyPaidByBenefits: boolean;
};
})
.catch((e) => {
console.error('Error initiating payment session:', e, JSON.stringify(Object.keys(e)));
return medusaError(e);
});
}
export async function initiatePaymentSession(
cart: HttpTypes.StoreCart,
data: HttpTypes.StoreInitializePaymentSession,

View File

@@ -337,6 +337,51 @@ export type Database = {
}
medreport: {
Tables: {
account_balance_entries: {
Row: {
id: string
account_id: string
amount: number
entry_type: string
description: string
source_company_id: string
reference_id: string
created_at: string
created_by: string
expires_at: string
is_active: boolean
is_analysis_order: boolean
is_analysis_package_order: boolean
}
Insert: {
account_id: string
amount: number
entry_type: string
description: string
source_company_id: string
reference_id: string
created_at: string
created_by: string
expires_at: string
is_active: boolean
is_analysis_order?: boolean
is_analysis_package_order?: boolean
}
Update: {
account_id?: string
amount?: number
entry_type?: string
description?: string
source_company_id?: string
reference_id?: string
created_at?: string
created_by?: string
expires_at?: string
is_active?: boolean
is_analysis_order?: boolean
is_analysis_package_order?: boolean
}
}
account_params: {
Row: {
account_id: string
@@ -2148,6 +2193,10 @@ export type Database = {
Args: { medusa_order_id: string }
Returns: boolean
}
process_periodic_benefit_distributions: {
Args: {}
Returns: void
}
revoke_nonce: {
Args: { p_id: string; p_reason?: string }
Returns: boolean