Java tutorial
/* * Odoo, Open Source Management Solution * Copyright (C) 2012-today Odoo SA (<http:www.odoo.com>) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http:www.gnu.org/licenses/> * */ package com.odoo.orm; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import odoo.OArguments; import odoo.ODomain; import odoo.Odoo; import org.json.JSONArray; import org.json.JSONObject; import android.content.Context; import android.text.TextUtils; import android.util.Log; import com.odoo.App; import com.odoo.base.ir.Attachments; import com.odoo.base.ir.IrAttachment; import com.odoo.base.ir.IrModel; import com.odoo.orm.ORelationRecordList.ORelationRecords; import com.odoo.support.OUser; import com.odoo.util.JSONUtils; import com.odoo.util.ODate; import com.odoo.util.PreferenceManager; /** * The Class OSyncHelper. */ public class OSyncHelper { /** The Constant TAG. */ public static final String TAG = OSyncHelper.class.getSimpleName(); /** The context. */ private Context mContext = null; /** The user. */ private OUser mUser = null; /** The model. */ private OModel mModel = null; /** The odoo. */ private Odoo mOdoo = null; /** The app. */ private App mApp = null; /** The relation record list. */ private ORelationRecordList mRelationRecordList = new ORelationRecordList(); /** The finished models. */ private List<String> mFinishedModels = new ArrayList<String>(); /** The finished rel models. */ private List<String> mFinishedRelModels = new ArrayList<String>(); /** The pref. */ private PreferenceManager mPref = null; /** The affected ids. */ private List<Integer> mAffectedIds = new ArrayList<Integer>(); /** The sync data limit. */ private Integer mSyncDataLimit = 0; /** * Instantiates a new o sync helper. * * @param context * the context * @param user * the user * @param model * the model */ public OSyncHelper(Context context, OUser user, OModel model) { mContext = context; mUser = user; mModel = model; mApp = (App) mContext.getApplicationContext(); if (mApp.inNetwork()) { mOdoo = mApp.getOdoo(); } } /** * Sync with server. * * @return true, if successful */ public boolean syncWithServer() { return syncWithServer(null); } /** * Sync with server. * * @param domain * the domain * @return true, if successful */ public boolean syncWithServer(ODomain domain) { return syncWithServer(mModel, domain); } /** * Sync with server. * * @param domain * the domain * @param checkForWriteCreateDate * the check for write create date * @return true, if successful */ public boolean syncWithServer(ODomain domain, Boolean checkForWriteCreateDate) { return syncWithServer(mModel, domain, checkForWriteCreateDate); } /** * Sync with server. * * @param model * the model * @param domain * the domain * @return true, if successful */ public boolean syncWithServer(OModel model, ODomain domain) { return syncWithServer(model, domain, true); } /** * Sync with server. * * @param model * the model * @param domain_filter * the domain_filter * @param checkForCreateWriteDate * the check for create write date * @return true, if successful */ public boolean syncWithServer(OModel model, ODomain domain_filter, Boolean checkForCreateWriteDate) { Log.v(TAG, "syncWithServer():" + model.getModelName()); Log.v(TAG, "User : " + mUser.getAndroidName()); if (!mFinishedModels.contains(model.getModelName()) || !checkForCreateWriteDate) { mFinishedModels.add(model.getModelName()); try { ODomain domain = new ODomain(); // Adding default domain to domain domain.append(model.defaultDomain()); if (checkForCreateWriteDate) { if (model.checkForCreateDate()) { // Adding Old data limit mPref = new PreferenceManager(mContext); int data_limit = mPref.getInt("sync_data_limit", 60); List<Integer> ids = model.ids(); if (ids.size() > 0 && model.checkForWriteDate() && !model.isEmptyTable()) domain.add("|"); if (ids.size() > 0) domain.add("&"); domain.add("create_date", ">=", ODate.getDateBefore(data_limit)); if (ids.size() > 0) domain.add("id", "not in", new JSONArray(ids.toString())); } // Adding Last sync date comparing with write_date of record if (model.checkForWriteDate() && !model.isEmptyTable()) { String last_sync_date = getLastSyncDate(model); domain.add("write_date", ">", last_sync_date); } } if (domain_filter != null) domain.append(domain_filter); JSONObject result = mOdoo.search_read(model.getModelName(), getFields(model), domain.get(), 0, mSyncDataLimit, null, null); if (checkForCreateWriteDate && model.checkForLocalLatestUpdate()) { handleResult(model, checkForLocalLatestUpdate(model, result)); } else { handleResult(model, result); } handleRelationRecords(model); // Creating record on server if model allows true if (model.canCreateOnServer()) createRecordOnserver(model); // Updating dirty record on server if model allows true if (model.canUpdateToServer()) updateToServer(model); // Deleting record from server if model allows true if (model.canDeleteFromServer()) deleteRecordFromServer(model); // Deleting record from local if model allows true if (model.canDeleteFromLocal()) deleteRecordInLocal(model); return syncFinish(model); } catch (Exception e) { e.printStackTrace(); } } else { return true; } return false; } /** * Delete record in local. * * @param model * the model */ private void deleteRecordInLocal(OModel model) { try { List<Integer> ids = model.ids(); ODomain domain = new ODomain(); domain.add("id", "in", new JSONArray(ids.toString())); JSONObject result = mOdoo.search_read(model.getModelName(), new JSONObject(), domain.get()); JSONArray records = result.getJSONArray("records"); if (records.length() > 0) { for (int i = 0; i < records.length(); i++) { Integer server_id = records.getJSONObject(i).getInt("id"); ids.remove(ids.indexOf(server_id)); } } model.checkInActiveRecord(true); for (Integer id : ids) model.delete("id = ? ", new Object[] { id }); model.checkInActiveRecord(false); } catch (Exception e) { e.printStackTrace(); } } /** * Check for dirty record in local and update to server. * * @param model * the model */ private void updateToServer(OModel model) { try { for (ODataRow row : model.select("is_dirty = ?", new Object[] { true })) { Integer recId = row.getInt("id"); JSONObject values = createJSONValues(model, row); if (values != null) { mOdoo.updateValues(model.getModelName(), values, recId); OValues vals = new OValues(); vals.put("is_dirty", "false"); model.update(vals, row.getInt("local_id")); } } } catch (Exception e) { e.printStackTrace(); } } /** * Check for local latest updated records. If result is from call_kw than * ignoring it. * * @param model * the model * @param result * the result * @return records object, which are new on server or latest updated on * server */ public JSONObject checkForLocalLatestUpdate(OModel model, JSONObject result) { JSONObject newResult = result; try { if (result.has("records") && result.getJSONArray("records").length() > 0) { newResult = new JSONObject(); JSONArray newORUpdateRecords = new JSONArray(); JSONArray records = result.getJSONArray("records"); List<Integer> mCheckIds = new ArrayList<Integer>(); HashMap<String, JSONObject> record_list = new HashMap<String, JSONObject>(); model.checkInActiveRecord(true); for (int i = 0; i < records.length(); i++) { JSONObject record = records.getJSONObject(i); if (model.hasRecord(record.getInt("id"))) { mCheckIds.add(record.getInt("id")); record_list.put("key_" + record.getInt("id"), record); } else { // New record. newORUpdateRecords.put(record); } } List<ODataRow> updateToServerRecordList = new ArrayList<ODataRow>(); if (mCheckIds.size() > 0) { // Getting write_date for records HashMap<String, String> write_dates = getWriteDate(model, mCheckIds); for (Integer id : mCheckIds) { String key = "KEY_" + id; String write_date = write_dates.get(key); ODataRow record = model.select("id = ? ", new Object[] { id }).get(0); String local_write_date = record.getString("local_write_date"); Date local_date = ODate.convertToDate(local_write_date, ODate.DEFAULT_FORMAT, true); Date server_date = ODate.convertToDate(write_date, ODate.DEFAULT_FORMAT, true); if (local_date.compareTo(server_date) > 0) { updateToServerRecordList.add(record); } else { newORUpdateRecords.put(record_list.get("key_" + id)); } } } if (updateToServerRecordList.size() > 0) { updateRecordOnServer(model, updateToServerRecordList); } Log.v(TAG, newORUpdateRecords.length() + " records new/update in local"); newResult.accumulate("records", newORUpdateRecords); model.checkInActiveRecord(false); } } catch (Exception e) { e.printStackTrace(); } return newResult; } /** * Update record on server. * * @param model * the model * @param records * the records */ private void updateRecordOnServer(OModel model, List<ODataRow> records) { try { for (ODataRow row : records) { JSONObject values = createJSONValues(model, row); if (values != null) { mOdoo.updateValues(model.getModelName(), values, row.getInt("id")); OValues vals = new OValues(); if (row.getBoolean("is_active")) { vals.put("is_dirty", "false"); } model.update(vals, row.getInt(OColumn.ROW_ID)); } } } catch (Exception e) { e.printStackTrace(); } } /** * Delete record from server. * * @param model * the model */ private void deleteRecordFromServer(OModel model) { try { model.checkInActiveRecord(true); for (ODataRow row : model.select("is_active = ? AND is_dirty = ?", new Object[] { false, true })) { if (mOdoo.unlink(model.getModelName(), row.getInt("id"))) { model.delete("id = ?", new Object[] { row.getInt("id") }, true); } } model.checkInActiveRecord(false); } catch (Exception e) { e.printStackTrace(); } } /** * Creates the record on server. * * @param model * the model */ private void createRecordOnserver(OModel model) { try { for (ODataRow row : model.select("id = ? ", new Object[] { 0 })) { create(model, row); } } catch (Exception e) { e.printStackTrace(); } } /** * Creates record on server * * @param data_row * the data_row */ public void create(ODataRow data_row) { create(mModel, data_row); } /** * Creates record on server * * @param model * the model * @param data_row * the data_row */ public Integer create(OModel model, ODataRow data_row) { try { JSONObject values = createJSONValues(model, data_row); if (model.getModelName().equals(new IrAttachment(mContext).getModelName())) { if (data_row.contains(Attachments.KEY_DB_DATAS)) values.put(Attachments.KEY_DB_DATAS, data_row.get(Attachments.KEY_DB_DATAS)); if (data_row.contains(Attachments.KEY_TYPE)) values.put(Attachments.KEY_TYPE, data_row.get(Attachments.KEY_TYPE)); } if (values != null) { Integer newId = 0; values.remove("id"); JSONObject result = mOdoo.createNew(model.getModelName(), values); newId = result.getInt("result"); OValues vals = new OValues(); vals.put("id", newId); vals.put("is_dirty", "false"); model.update(vals, data_row.getInt(OColumn.ROW_ID)); return newId; } } catch (Exception e) { e.printStackTrace(); } return null; } /** * Creates the json values. * * @param model * the model * @param row * the row * @return the JSON object */ private JSONObject createJSONValues(OModel model, ODataRow row) { JSONObject values = null; try { values = new JSONObject(); for (OColumn col : model.getColumns(false)) { if (col.getRelationType() == null) { Object val = row.get(col.getName()); if (val == null || val.toString().equals("false") || TextUtils.isEmpty(val.toString())) val = false; values.put(col.getName(), val); } else { // Relation columns switch (col.getRelationType()) { case ManyToOne: ODataRow m2o = row.getM2ORecord(col.getName()).browse(); if (m2o != null) values.put(col.getName(), m2o.getInt("id")); break; case OneToMany: JSONArray o2mRecords = new JSONArray(); List<ODataRow> o2mRecordList = row.getO2MRecord(col.getName()).browseEach(); if (o2mRecordList.size() > 0) { JSONArray rec_ids = new JSONArray(); for (ODataRow o2mR : o2mRecordList) { if (o2mR.getInt("id") != 0) rec_ids.put(o2mR.getInt("id")); } o2mRecords.put(6); o2mRecords.put(false); o2mRecords.put(rec_ids); values.put(col.getName(), new JSONArray().put(o2mRecords)); } break; case ManyToMany: JSONArray m2mRecords = new JSONArray(); List<ODataRow> m2mRecordList = row.getM2MRecord(col.getName()).browseEach(); if (m2mRecordList.size() > 0) { JSONArray rec_ids = new JSONArray(); for (ODataRow o2mR : m2mRecordList) { if (o2mR.getInt("id") != 0) rec_ids.put(o2mR.getInt("id")); } m2mRecords.put(6); m2mRecords.put(false); m2mRecords.put(rec_ids); values.put(col.getName(), new JSONArray().put(m2mRecords)); } break; } } } } catch (Exception e) { e.printStackTrace(); } return values; } /** * Sync with method. * * @param method * the method * @param args * the args * @return true, if successful */ public boolean syncWithMethod(String method, OArguments args) { boolean synced = false; try { JSONObject result = mOdoo.call_kw(mModel.getModelName(), method, args.getArray()); handleResult(mModel, result); handleRelationRecords(mModel); } catch (Exception e) { e.printStackTrace(); } return synced; } public void quickStoreRecords(ODataRow row) { Log.v(TAG, "quickStoreRecords"); try { JSONObject records = new JSONObject(); records.put("records", new JSONArray().put(JSONUtils.toJSONObject(row))); handleResult(mModel, records); handleRelationRecords(mModel); } catch (Exception e) { e.printStackTrace(); } } /** * Gets the last sync date. * * @param model * the model * @return the last sync date */ public String getLastSyncDate(OModel model) { String last_sync_date = "false"; IrModel irModel = new IrModel(mContext); List<ODataRow> records = irModel.select("model = ?", new Object[] { model.getModelName() }); if (records.size() > 0) { last_sync_date = records.get(0).getString("last_synced"); } if (last_sync_date.equals("false")) last_sync_date = ODate.getDate(); return last_sync_date; } /** * Handle relation records. * * @param model * the model */ private void handleRelationRecords(OModel model) { List<String> keys = new ArrayList<String>(); keys.addAll(mRelationRecordList.keys()); for (String key : keys) { if (!mFinishedRelModels.contains(key)) { mFinishedRelModels.add(key); ORelationRecords rel = mRelationRecordList.get(key); OModel base_model = rel.getBaseModel(); base_model.setSyncingDataFlag(true); OModel rel_model = rel.getRelModel(); rel_model.setSyncingDataFlag(true); ODomain rel_domain = new ODomain(); if (rel.getRelIds().size() > 0) { rel_domain.add("id", "in", rel.getRelIds()); syncWithServer(rel_model, rel_domain, false); } } } } /** * Sync finish. * * @param model * the model * @return true, if successful */ private boolean syncFinish(OModel model) { String finish_date_time = ODate.getDate(); Log.v(TAG, model.getModelName() + " sync finished at " + finish_date_time + " (UTC)"); IrModel irmodel = new IrModel(mContext); OValues values = new OValues(); values.put("last_synced", finish_date_time); irmodel.update(values, "model = ?", new Object[] { model.getModelName() }); return true; } /** * Creates the value row. * * @param model * the model * @param columns * the columns * @param original_record * the original_record * @return the o values */ private OValues createValueRow(OModel model, List<OColumn> columns, JSONObject original_record) { OValues values = new OValues(); try { List<Integer> r_ids = new ArrayList<Integer>(); for (OColumn column : columns) { JSONObject record = model.beforeCreateRow(column, original_record); if (column.getRelationType() != null) { // Relation records switch (column.getRelationType()) { case ManyToOne: /* * Handling ManyToOne records */ OModel m2o = model.createInstance(column.getType()); String rel_key = m2o.getTableName() + "_" + column.getName(); if (record.get(column.getName()) instanceof JSONArray) { JSONArray m2oRecord = record.getJSONArray(column.getName()); // Local table contains only id and name so not // required // to request on server if (m2o.getColumns(false).size() > 2 || (m2o.getColumns(false).size() > 4 && model.getOdooVersion().getVersion_number() > 7)) { // Need to create list of ids for model ORelationRecords rel_record = mRelationRecordList.new ORelationRecords(); if (mRelationRecordList.contains(rel_key)) { rel_record = mRelationRecordList.get(rel_key); } else { rel_record.setRelModel(m2o); rel_record.setBaseModel(model); } rel_record.setRefColumn(column.getName()); rel_record.setRelationType(column.getRelationType()); rel_record.addBaseRelId(record.getInt("id"), m2oRecord.getInt(0)); // Creating relation ids list for relation model mRelationRecordList.add(rel_key, rel_record); } OValues m2oVals = new OValues(); m2oVals.put("id", m2oRecord.get(0)); m2oVals.put("name", m2oRecord.get(1)); m2oVals.put("is_dirty", false); Integer row_id = m2o.createORReplace(m2oVals); // Replacing original id with row_id to maintain // relation for local m2oRecord.put(0, row_id); record.put(column.getName(), m2oRecord); values.put(column.getName(), m2oRecord.get(0)); } break; case ManyToMany: r_ids.clear(); OModel m2m = model.createInstance(column.getType()); rel_key = m2m.getTableName() + "_" + column.getName(); JSONArray ids_list = record.getJSONArray(column.getName()); int len = ids_list.length(); // limiting sync limit for many to many int record_len = column.getRecordSyncLimit(); if (record_len != -1 && len > record_len) len = record_len; List<Integer> row_ids = new ArrayList<Integer>(); for (int i = 0; i < len; i++) { int server_id = ids_list.getInt(i); r_ids.add(server_id); OValues vals = new OValues(); vals.put("id", server_id); int row_id = m2m.createORReplace(vals); row_ids.add(row_id); } values.put(column.getName(), row_ids); ORelationRecords mrel_record = mRelationRecordList.new ORelationRecords(); if (mRelationRecordList.contains(rel_key)) { mrel_record = mRelationRecordList.get(rel_key); } else { mrel_record.setRelModel(m2m); mrel_record.setBaseModel(model); } mrel_record.addBaseRelId(record.getInt("id"), r_ids); mrel_record.setRefColumn(column.getName()); mrel_record.setRelationType(column.getRelationType()); // Creating relation ids list for relation model mRelationRecordList.add(rel_key, mrel_record); break; case OneToMany: OModel o2m = model.createInstance(column.getType()); rel_key = o2m.getTableName() + "_" + column.getName(); JSONArray o2m_ids_list = record.getJSONArray(column.getName()); r_ids.clear(); for (int i = 0; i < o2m_ids_list.length(); i++) { r_ids.add(o2m_ids_list.getInt(i)); } // Need to create list of ids for model ORelationRecords rel_record = mRelationRecordList.new ORelationRecords(); if (mRelationRecordList.contains(rel_key)) { rel_record = mRelationRecordList.get(rel_key); } else { rel_record.setRelModel(o2m); rel_record.setBaseModel(model); } rel_record.addBaseRelId(record.getInt("id"), r_ids); rel_record.setRefColumn(column.getName()); rel_record.setRelationType(column.getRelationType()); // Creating relation ids list for relation model mRelationRecordList.add(rel_key, rel_record); break; } } else { // General record values.put(column.getName(), record.get(column.getName())); } } values.put("is_dirty", false); } catch (Exception e) { e.printStackTrace(); } return values; } /** * Handle result. * * @param model * the model * @param result * the result */ public void handleResult(OModel model, JSONObject result) { try { JSONArray records = (result.has("result")) ? result.getJSONArray("result") : result.getJSONArray("records"); List<OValues> values_list = new ArrayList<OValues>(); for (int i = 0; i < records.length(); i++) { JSONObject record = records.getJSONObject(i); OValues vals = createValueRow(model, model.getColumns(false), record); values_list.add(vals); } // Creating new records. List<Integer> affectedIds = model.createORReplace(values_list); mAffectedIds.clear(); mAffectedIds.addAll(affectedIds); } catch (Exception e) { e.printStackTrace(); } } /** * Gets the affected ids. * * @return the affected ids */ public List<Integer> getAffectedIds() { return mAffectedIds; } /** * Gets the fields. * * @param model * the model * @return the fields */ public JSONObject getFields(OModel model) { JSONObject fields = new JSONObject(); try { for (OColumn column : model.getColumns(false)) { if (!column.getName().equals("id")) fields.accumulate("fields", column.getName()); } } catch (Exception e) { e.printStackTrace(); } return fields; } /** * Model info. * * @param models * the models * @return the list */ public List<OValues> modelInfo(List<String> models) { List<OValues> models_list = new ArrayList<OValues>(); try { ODomain domain = new ODomain(); domain.add("name", "in", new JSONArray(models.toString())); JSONObject result = mOdoo.search_read("ir.module.module", getFields(mModel), domain.get()); JSONArray records = result.getJSONArray("records"); for (int i = 0; i < records.length(); i++) { JSONObject record = records.getJSONObject(i); OValues values = createValueRow(mModel, mModel.getColumns(false), record); models_list.add(values); } } catch (Exception e) { e.printStackTrace(); } return models_list; } /** * Gets the context. * * @param obj * the obj * @return the context */ public JSONObject getContext(JSONObject obj) { try { return mOdoo.updateContext((obj != null) ? obj : new JSONObject()); } catch (Exception e) { e.printStackTrace(); return new JSONObject(); } } /** * Gets the write date. * * @param model * the model * @param ids * the ids * @return the write date */ private HashMap<String, String> getWriteDate(OModel model, List<Integer> ids) { HashMap<String, String> map = new HashMap<String, String>(); try { JSONArray results = new JSONArray(); if (model.getPermReadColumn("write_date") != null) { JSONObject fields = new JSONObject(); fields.accumulate("fields", "write_date"); ODomain domain = new ODomain(); domain.add("id", "in", ids); JSONObject result = mOdoo.search_read(model.getModelName(), fields, domain.get()); results = result.getJSONArray("records"); } else { JSONObject write_date_list = perm_read(model, ids); results = write_date_list.getJSONArray("result"); } if (results.length() > 0) { for (int i = 0; i < results.length(); i++) { JSONObject obj = results.getJSONObject(i); map.put("KEY_" + obj.getInt("id"), obj.getString("write_date")); } } } catch (Exception e) { e.printStackTrace(); } return map; } /** * Sync data limit. * * @param dataLimit * the data limit * @return the o sync helper */ public OSyncHelper syncDataLimit(Integer dataLimit) { mSyncDataLimit = dataLimit; return this; } /** * Perm_read. * * @param model * the model * @param ids * the ids * @return the JSON object */ private JSONObject perm_read(OModel model, List<Integer> ids) { try { return mOdoo.perm_read(model.getModelName(), ids); } catch (Exception e) { e.printStackTrace(); } return null; } /** * Call method. * * @param method * the method * @param args * the args * @return the object */ public Object callMethod(String method, OArguments args) { return callMethod(method, args, null, null); } /** * Call method. * * @param method * the method * @param args * the args * @param context * the context * @return the object */ public Object callMethod(String method, OArguments args, JSONObject context) { return callMethod(mModel.getModelName(), method, args, context, null); } public Object callMethod(String method, OArguments args, JSONObject context, JSONObject kwargs) { return callMethod(mModel.getModelName(), method, args, context, kwargs); } /** * Call method. * * @param method * the method * @param args * the args * @param context * the context * @param kwargs * the kwargs * @return the object */ public Object callMethod(String model, String method, OArguments args, JSONObject context, JSONObject kwargs) { try { if (kwargs == null) kwargs = new JSONObject(); if (context != null) { args.add(mOdoo.updateContext(context)); } JSONObject result = mOdoo.call_kw(model, method, args.getArray(), kwargs); if (result.has("result")) { return result.get("result"); } } catch (Exception e) { e.printStackTrace(); } return false; } public ServerDataHelper dataHelper() { return new ServerDataHelper(mContext, mModel, mOdoo); } }