Gabung app Android Flutter 'Saat Invest' ke repo saat-app (folder flutter/)

Satu repo untuk backend Odoo (addon saat_invest) + app Android.
- flutter/ berisi project Flutter (lib/, android/, pubspec, test)
- Login Odoo JSON-RPC, tab Portofolio+Equity, tab Transaksi, catat jual/beli otomatis sync
- Build .gitignore nested dipakai sehingga artefak build/.dart_tool tak ter-track
Sejarah: gabung flat (satu sejarah backend+app).
This commit is contained in:
2026-09-02 14:48:32 +07:00
parent b53406791f
commit 5ab72678a3
34 changed files with 1881 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
/// Klien JSON-RPC utk Odoo 17. Authenticate + execute_kw.
class OdooClient {
OdooClient({required this.baseUrl, required this.db, required this.username, required this.password})
: _http = http.Client();
final String baseUrl; // mis: http://192.168.x.x:18069
final String db; // nama db, default 'odoo'
final String username;
final String password;
final http.Client _http;
int? _uid;
Uri get _endpoint => Uri.parse('$baseUrl/jsonrpc');
Future<int> authenticate() async {
final result = await _call('common', 'authenticate', [db, username, password, {}]);
if (result is int && result > 0) {
_uid = result;
return result;
}
throw Exception('Login gagal: periksa kredensial / URL Odoo');
}
Future<dynamic> executeKwd(String model, String method,
{List<dynamic> args = const [], Map<String, dynamic>? kwargs}) async {
_uid ??= await authenticate();
final params = [
db,
_uid,
password,
model,
method,
args,
if (kwargs != null) kwargs,
];
return _call('object', 'execute_kw', params);
}
Future<dynamic> _call(String service, String method, List<dynamic> args) async {
final body = {
'jsonrpc': '2.0',
'method': 'call',
'params': {
'service': service,
'method': method,
'args': args,
},
};
try {
final resp = await _http
.post(_endpoint,
headers: {'Content-Type': 'application/json'}, body: jsonEncode(body))
.timeout(const Duration(seconds: 25));
final json = jsonDecode(resp.body) as Map<String, dynamic>;
if (json.containsKey('error')) {
final err = json['error'] as Map?;
String? message = (err?['data'] as Map?)?['message'] as String? ??
(err?['message'] as String?) ??
'Unknown Odoo error';
throw Exception('Odoo error: $message');
}
return json['result'];
} on TimeoutException {
throw Exception('Timeout — pastikan server Odoo:18069 tercapai');
} on http.ClientException catch (e) {
throw Exception('Koneksi gagal: ${e.message}');
}
}
void close() => _http.close();
}