odoo_loyalty_app/lib/services/odoo_service.dart

269 lines
7.8 KiB
Dart

import 'package:odoo_rpc/odoo_rpc.dart';
import 'config.dart';
import 'theme_manager.dart';
class OdooService {
static final OdooService _instance = OdooService._internal();
factory OdooService() => _instance;
OdooService._internal();
OdooClient? client;
void connect(String url, {OdooSession? session}) {
client = OdooClient(url, sessionId: session);
}
/// Returns the session cookie header value for authenticated image loading.
String get sessionCookie {
final sessionId = client?.sessionId?.id ?? '';
return 'session_id=$sessionId';
}
/// Returns the URL for the full notification image.
String notificationImageUrl(int notifId) =>
'${AppConfig.odooUrl}/web/image/mapan.app.notification/$notifId/image';
/// Returns the URL for a carousel image (uploaded).
String carouselImageUrl(int slideId) =>
'${AppConfig.odooUrl}/web/image/mapan.app.carousel/$slideId/image';
/// Returns the URL for a promo image (uploaded).
String promoImageUrl(int promoId) =>
'${AppConfig.odooUrl}/web/image/mapan.app.promo/$promoId/image';
Future<OdooSession> login(String db, String username, String password) async {
if (client == null) throw Exception("Connect to Odoo first");
return await client!.authenticate(db, username, password);
}
Future<List<dynamic>> getLoyaltyCards(int partnerId) async {
if (client == null) throw Exception("Connect to Odoo first");
// Only fetch cards from 'loyalty' type programs (multi-tier: Silver/Gold/Platinum).
// Excludes subscriptions, coupons, gift cards, promotions, eWallets, etc.
return await client!.callKw({
'model': 'loyalty.card',
'method': 'search_read',
'args': [
[
['partner_id', '=', partnerId],
['program_id.program_type', '=', 'loyalty'],
['program_id.active', '=', true],
],
],
'kwargs': {
'fields': [
'points',
'program_id',
'code',
'subscription_start_date',
'subscription_end_date',
]
}
}) as List<dynamic>;
}
/// Fetch subscription cards only — displayed as a "My Subscriptions" list.
Future<List<dynamic>> getSubscriptionCards(int partnerId) async {
if (client == null) throw Exception("Connect to Odoo first");
return await client!.callKw({
'model': 'loyalty.card',
'method': 'search_read',
'args': [
[
['partner_id', '=', partnerId],
['program_id.program_type', '=', 'subscription'],
['program_id.active', '=', true],
],
],
'kwargs': {
'fields': [
'points',
'program_id',
'code',
'subscription_start_date',
'subscription_end_date',
]
}
}) as List<dynamic>;
}
/// Fetch carousel slides and promo highlights for the home screen.
Future<Map<String, dynamic>> getCmsContent() async {
if (client == null) throw Exception("Connect to Odoo first");
final response = await client!.callRPC(
'/api/loyalty/cms_content',
'call',
{},
);
if (response != null && response['status'] == 'success') {
return response as Map<String, dynamic>;
}
return {'carousel': [], 'promos': []};
}
/// Fetch app configuration (About Us URL, Contact Us URL, Branding & Theme).
static Future<Map<String, String>> getAppConfig() async {
final activeClient = OdooService().client;
final clientToUse = activeClient ?? OdooClient(AppConfig.odooUrl);
try {
final res = await clientToUse.callRPC('/api/loyalty/app_config', 'call', {});
if (res != null && res['status'] == 'success') {
final configMap = {
'about_us_url': (res['about_us_url'] as String?) ?? '',
'contact_us_url': (res['contact_us_url'] as String?) ?? '',
'brand_logo': (res['brand_logo'] as String?) ?? '',
'primary_color': (res['primary_color'] as String?) ?? '#C62828',
'secondary_color': (res['secondary_color'] as String?) ?? '#FF8F00',
};
// Save and apply new branding and theme colors dynamically
await ThemeManager.instance.updateConfig(
primaryHex: configMap['primary_color']!,
secondaryHex: configMap['secondary_color']!,
brandLogoB64: configMap['brand_logo']!,
);
return configMap;
}
return {'about_us_url': '', 'contact_us_url': ''};
} catch (_) {
return {'about_us_url': '', 'contact_us_url': ''};
} finally {
if (activeClient == null) {
clientToUse.close();
}
}
}
/// Fetch loyalty point history for the current user.
Future<List<dynamic>> getOrderHistory() async {
if (client == null) throw Exception("Connect to Odoo first");
final response = await client!.callRPC(
'/api/loyalty/order_history',
'call',
{},
);
if (response != null && response['status'] == 'success') {
return response['data'] as List<dynamic>;
}
return [];
}
Future<dynamic> sendOtp({
String? email,
String? phone,
String? phoneOrEmail,
required String type,
}) async {
if (client == null) throw Exception("Connect to Odoo first");
return await client!.callRPC(
'/api/loyalty/send_otp',
'call',
{
if (email != null) 'email': email,
if (phone != null) 'phone': phone,
if (phoneOrEmail != null) 'phone_or_email': phoneOrEmail,
'type': type,
},
);
}
Future<dynamic> signUpMember({
required String name,
required String phone,
required String email,
required String birthDate,
required String gender,
required String password,
required String otp,
}) async {
if (client == null) throw Exception("Connect to Odoo first");
return await client!.callRPC(
'/api/loyalty/signup_member',
'call',
{
'name': name,
'phone': phone,
'email': email,
'birth_date': birthDate,
'gender': gender,
'password': password,
'otp': otp,
},
);
}
Future<dynamic> activateAccount({
required String phone,
required String email,
required String birthDate,
required String password,
required String otp,
}) async {
if (client == null) throw Exception("Connect to Odoo first");
return await client!.callRPC(
'/api/loyalty/activate_account',
'call',
{
'phone': phone,
'email': email,
'birth_date': birthDate,
'password': password,
'otp': otp,
},
);
}
Future<dynamic> resetPassword({
required String phoneOrEmail,
required String otp,
required String password,
}) async {
if (client == null) throw Exception("Connect to Odoo first");
return await client!.callRPC(
'/api/loyalty/reset_password',
'call',
{
'phone_or_email': phoneOrEmail,
'otp': otp,
'password': password,
},
);
}
Future<dynamic> deleteAccount(String password) async {
if (client == null) throw Exception("Connect to Odoo first");
return await client!.callRPC(
'/api/loyalty/delete_account',
'call',
{
'password': password,
},
);
}
/// Fetch public branch information (includes lat/lng for geolocation sorting).
static Future<List<dynamic>> getPublicBranches() async {
final activeClient = OdooService().client;
final clientToUse = activeClient ?? OdooClient(AppConfig.odooUrl);
try {
final res = await clientToUse.callRPC(
'/api/loyalty/branches',
'call',
{}
);
if (res != null && res['status'] == 'success') {
return res['data'] as List<dynamic>;
} else {
throw Exception(res?['message'] ?? 'Failed to load branches');
}
} catch (e) {
rethrow;
} finally {
if (activeClient == null) {
clientToUse.close();
}
}
}
}