import 'package:odoo_rpc/odoo_rpc.dart'; import 'config.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. /// Usage: Image.network(url, headers: {'Cookie': OdooService().sessionCookie}) 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'; Future 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> getLoyaltyCards(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]], ], 'kwargs': {'fields': ['points', 'program_id', 'code']} }) as List; } Future 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 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 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 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 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 using our secure Odoo endpoint /// This completely isolates the Admin API Key from the Flutter Source Code! static Future> getPublicBranches() async { final tempClient = OdooClient(AppConfig.odooUrl); try { final res = await tempClient.callRPC( '/api/loyalty/branches', 'call', {} ); if (res != null && res['status'] == 'success') { return res['data'] as List; } else { throw Exception(res?['message'] ?? 'Failed to load branches'); } } catch (e) { rethrow; } finally { tempClient.close(); } } }