fix searchRead

This commit is contained in:
2026-09-04 17:59:58 +07:00
parent 1f69f922be
commit 47c8688884
3 changed files with 242 additions and 34 deletions

View File

@@ -1,7 +1,9 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:http/http.dart' as http;
import '../odoo_rpc_helper.dart';
/// Klien JSON-RPC utk Odoo 17. Authenticate + execute_kw.
class OdooClient {
@@ -42,6 +44,68 @@ class OdooClient {
return _call('object', 'execute_kw', params);
}
Future<dynamic> executeKwdWithArgs(String model, String method,
{List<dynamic> fields = const [], Map<String, dynamic>? kwargs}) async {
_uid ??= await authenticate();
final args = [
db,
_uid,
password,
model,
method,
[],
fields,
if (kwargs != null) kwargs,
];
return _call('object', 'execute_kw', args);
}
Future<dynamic> executeSearchReadPayload(String model, List<String> fields) async {
int uid = await authenticate();
final payload = OdooRpcHelper.searchRead(
db: db,
uid: uid,
password: password,
model: model,
domain: [[]],
fields: fields,
limit: 20,
);
return _callSearchRead(payload);
}
Future<dynamic> _callSearchRead(Map<String, dynamic> payload) async{
// Use jsonEncode along with a JsonEncoder for indentation
JsonEncoder encoder = const JsonEncoder.withIndent(' ');
String prettyString = encoder.convert(payload);
debugPrint(prettyString);
try {
final resp = await _http
.post(_endpoint,
headers: {'Content-Type': 'application/json'}, body: jsonEncode(payload))
.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}');
}
}
Future<dynamic> _call(String service, String method, List<dynamic> args) async {
final body = {
'jsonrpc': '2.0',
@@ -52,6 +116,13 @@ class OdooClient {
'args': args,
},
};
// Use jsonEncode along with a JsonEncoder for indentation
JsonEncoder encoder = const JsonEncoder.withIndent(' ');
String prettyString = encoder.convert(body);
debugPrint(prettyString);
try {
final resp = await _http
.post(_endpoint,

View File

@@ -0,0 +1,88 @@
import 'dart:convert';
class OdooRpcHelper {
static const String _jsonRpcVersion = "2.0";
static const String _method = "call";
/// Builds the correct JSON-RPC payload for any Odoo model method
static Map<String, dynamic> buildExecuteKw({
required String db,
required int uid,
required String password,
required String model,
required String method,
List<dynamic> domain = const [],
Map<String, dynamic> kwargs = const {},
}) {
// Make sure domain is always a list of lists (even if empty)
final safeDomain = domain.isEmpty ? [[]] : domain;
return {
"jsonrpc": _jsonRpcVersion,
"method": _method,
"params": {
"service": "object",
"method": "execute_kw",
"args": [
db,
uid,
password,
model,
method,
safeDomain,
kwargs,
],
},
"id": DateTime.now().millisecondsSinceEpoch,
};
}
/// Convenience wrapper for search_read
static Map<String, dynamic> searchRead({
required String db,
required int uid,
required String password,
required String model,
List<dynamic> domain = const [],
List<String> fields = const [],
int? limit,
int? offset,
String? order,
}) {
final kwargs = <String, dynamic>{};
if (fields.isNotEmpty) kwargs["fields"] = fields;
if (limit != null) kwargs["limit"] = limit;
if (offset != null) kwargs["offset"] = offset;
if (order != null) kwargs["order"] = order;
return buildExecuteKw(
db: db,
uid: uid,
password: password,
model: model,
method: "search_read",
domain: domain,
kwargs: kwargs,
);
}
/// Convenience wrapper for create
static Map<String, dynamic> create({
required String db,
required int uid,
required String password,
required String model,
required Map<String, dynamic> values,
}) {
return buildExecuteKw(
db: db,
uid: uid,
password: password,
model: model,
method: "create",
domain: [[]], // create doesn't use domain
kwargs: {"vals": values}, // Odoo expects 'vals' for create in some versions
);
}
}

View File

@@ -9,51 +9,100 @@ class StockService {
final OdooClient client;
// ---- Saham ----
// Future<List<Saham>> listSahamOld() async {
// final res = await client.executeKwd(
// 'saat.saham',
// 'search_read',
// args: [
// "saat.saham",
// "search_read",
// [[]],
// {'fields': ['name', 'perusahaan', 'sector'], "limit": 100, "offset": 0, "order": "name asc"},
// ],
// );
// return (res as List).map((e) => Saham.fromOdoo(e as Map<String, dynamic>)).toList();
// }
//
// Future<List<Saham>> listSaham() async {
// final res = await client.executeKwdWithArgs(
// 'saat.saham',
// 'search_read',
// fields: [
// {'fields': ['name', 'perusahaan', 'sector'],}
// ]
// );
// return (res as List).map((e) => Saham.fromOdoo(e as Map<String, dynamic>)).toList();
// }
Future<List<Saham>> listSaham() async {
final res = await client.executeKwd(
'saat.saham',
'search_read',
args: [
[],
{'fields': ['name', 'perusahaan', 'sector']},
],
);
final fields = ['name', 'perusahaan', 'sector'];
final res = await client.executeSearchReadPayload("saat.saham", fields);
return (res as List).map((e) => Saham.fromOdoo(e as Map<String, dynamic>)).toList();
}
// ---- Portofolio ----
// Future<List<Portfolio>> listPortofolio() async {
// final res = await client.executeKwd(
// 'saat.portofolio',
// 'search_read',
// args: [
// [],
// {
// 'fields': [
// 'saham_id', 'sisa_lembar', 'avg_cost', 'harga_sekarang',
// 'nilai_sekarang', 'realized_pl', 'unrealized_pl',
// ]
// },
// ],
// );
// return (res as List).map((e) => Portfolio.fromOdoo(e as Map<String, dynamic>)).toList();
// }
// Future<List<Portfolio>> listPortofolio() async {
// final res = await client.executeKwdWithArgs(
// 'saat.portofolio',
// 'search_read',
// fields: [
// {
// "fields": ["saham_id", "sisa_lembar", "avg_cost", "harga_sekarang",
// "nilai_sekarang", "realized_pl", "unrealized_pl"],
// "limit": 20,
// "offset": 0
// }
// ],
// );
// return (res as List).map((e) => Portfolio.fromOdoo(e as Map<String, dynamic>)).toList();
// }
Future<List<Portfolio>> listPortofolio() async {
final res = await client.executeKwd(
'saat.portofolio',
'search_read',
args: [
[],
{
'fields': [
'saham_id', 'sisa_lembar', 'avg_cost', 'harga_sekarang',
'nilai_sekarang', 'realized_pl', 'unrealized_pl',
]
},
],
);
final fields = [
'saham_id', 'sisa_lembar', 'avg_cost', 'harga_sekarang',
'nilai_sekarang', 'realized_pl', 'unrealized_pl',
];
final res = await client.executeSearchReadPayload("saat.portofolio", fields);
return (res as List).map((e) => Portfolio.fromOdoo(e as Map<String, dynamic>)).toList();
}
// ---- Transaksi ----
// Future<List<Transaksi>> listTransaksi() async {
// final res = await client.executeKwdWithArgs(
// 'saat.transaksi',
// 'search_read',
// fields: [
// {
// 'fields': [
// 'tanggal', 'tipe', 'saham_id', 'volume', 'harga', 'fee', 'total'
// ],
// 'order': 'tanggal desc, id desc',
// },
// ],
// );
// return (res as List).map((e) => Transaksi.fromOdoo(e as Map<String, dynamic>)).toList();
// }
Future<List<Transaksi>> listTransaksi() async {
final res = await client.executeKwd(
'saat.transaksi',
'search_read',
args: [
[],
{
'fields': [
'tanggal', 'tipe', 'saham_id', 'volume', 'harga', 'fee', 'total'
],
'order': 'tanggal desc, id desc',
},
],
);
final fields = ['tanggal', 'tipe', 'saham_id', 'volume', 'harga', 'fee', 'total'];
final res = await client.executeSearchReadPayload("saat.transaksi", fields);
return (res as List).map((e) => Transaksi.fromOdoo(e as Map<String, dynamic>)).toList();
}