com.odoo.core.orm.OModel.java Source code

Java tutorial

Introduction

Here is the source code for com.odoo.core.orm.OModel.java

Source

/**
 * 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/>
 *
 * Created on 30/12/14 3:31 PM
 */
package com.odoo.core.orm;

import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SyncResult;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;

import com.odoo.App;
import com.odoo.base.addons.ir.IrModel;
import com.odoo.core.auth.OdooAccountManager;
import com.odoo.core.orm.annotation.Odoo;
import com.odoo.core.orm.fields.OColumn;
import com.odoo.core.orm.fields.types.OBoolean;
import com.odoo.core.orm.fields.types.ODateTime;
import com.odoo.core.orm.fields.types.OInteger;
import com.odoo.core.orm.fields.types.OSelection;
import com.odoo.core.orm.provider.BaseModelProvider;
import com.odoo.core.service.ISyncServiceListener;
import com.odoo.core.service.OSyncAdapter;
import com.odoo.core.support.OUser;
import com.odoo.core.support.OdooFields;
import com.odoo.core.utils.OCursorUtils;
import com.odoo.core.utils.ODateUtils;
import com.odoo.core.utils.OListUtils;
import com.odoo.core.utils.OPreferenceManager;
import com.odoo.core.utils.OStorageUtils;
import com.odoo.core.utils.StringUtils;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;

import odoo.ODomain;
import odoo.OdooVersion;

public class OModel implements ISyncServiceListener {

    public static final String TAG = OModel.class.getSimpleName();
    public String BASE_AUTHORITY = "com.odoo.crm.core.provider.content";
    public static final String KEY_UPDATE_IDS = "key_update_ids";
    public static final String KEY_INSERT_IDS = "key_insert_ids";
    public static final int INVALID_ROW_ID = -1;
    public static OSQLite sqLite = null;
    private Context mContext;
    private OUser mUser;
    private String model_name = null;
    private List<OColumn> mColumns = new ArrayList<>();
    private List<OColumn> mRelationColumns = new ArrayList<>();
    private List<OColumn> mFunctionalColumns = new ArrayList<>();
    private HashMap<String, Field> mDeclaredFields = new HashMap<>();
    private OdooVersion mOdooVersion = null;
    private String default_name_column = "name";
    public static OModelRegistry modelRegistry = new OModelRegistry();
    private boolean hasMailChatter = false;

    // Relation record command
    public enum Command {
        Add(0), Update(1), Delete(2), Replace(6);

        int type;

        Command(int type) {
            this.type = type;
        }

        public int getValue() {
            return type;
        }
    }

    // Base Columns
    OColumn id = new OColumn("ID", OInteger.class).setDefaultValue(0);
    @Odoo.api.v8
    @Odoo.api.v9alpha
    public OColumn create_date = new OColumn("Created On", ODateTime.class);

    @Odoo.api.v8
    @Odoo.api.v9alpha
    public OColumn write_date = new OColumn("Last Updated On", ODateTime.class);

    // Local Base columns
    OColumn _id = new OColumn("_ID", OInteger.class).setAutoIncrement().setLocalColumn();
    OColumn _write_date = new OColumn("Local Write Date", ODateTime.class).setLocalColumn();
    OColumn _is_dirty = new OColumn("Dirty record", OBoolean.class).setDefaultValue(false).setLocalColumn();
    OColumn _is_active = new OColumn("Active Record", OBoolean.class).setDefaultValue(true).setLocalColumn();

    public OModel(Context context, String model_name, OUser user) {
        mContext = context;
        mUser = (user == null) ? OUser.current(context) : user;
        this.model_name = model_name;
        if (mUser != null) {
            mOdooVersion = new OdooVersion();
            mOdooVersion.setVersion_number(mUser.getVersion_number());
            mOdooVersion.setServer_serie(mUser.getVersion_serie());
            if (sqLite == null) {
                sqLite = new OSQLite(mContext, mUser);
            }
        }
    }

    public SQLiteDatabase getReadableDatabase() {
        return sqLite.getReadableDatabase();
    }

    public SQLiteDatabase getWritableDatabase() {
        return sqLite.getWritableDatabase();
    }

    public String getDatabaseName() {
        return sqLite.getDatabaseName();
    }

    public void close() {
        // Any operation when closing database
    }

    public void setDefaultNameColumn(String nameColumn) {
        default_name_column = nameColumn;
    }

    public String getDefaultNameColumn() {
        return default_name_column;
    }

    public OModel setModelName(String model_name) {
        this.model_name = model_name;
        return this;
    }

    public boolean hasMailChatter() {
        return hasMailChatter;
    }

    public void setHasMailChatter(boolean hasMailChatter) {
        this.hasMailChatter = hasMailChatter;
    }

    public OUser getUser() {
        return mUser;
    }

    public OdooVersion getOdooVersion() {
        return mOdooVersion;
    }

    public List<OColumn> getColumns() {
        if (mColumns.size() == 0) {
            prepareFields();
        }
        return mColumns;
    }

    public List<OColumn> getColumns(Boolean local) {
        if (local != null) {
            List<OColumn> cols = new ArrayList<>();
            for (OColumn column : getColumns())
                if (local == column.isLocal())
                    cols.add(column);
            return cols;
        } else {
            return mColumns;
        }
    }

    public List<OColumn> getRelationColumns() {
        if (mColumns.size() <= 0)
            prepareFields();
        return mRelationColumns;
    }

    public OColumn getColumn(String column_name) {
        if (mDeclaredFields.size() <= 0)
            prepareFields();
        Field filed = mDeclaredFields.get(column_name);
        return getColumn(filed);
    }

    private OColumn getColumn(Field field) {
        OColumn column = null;
        if (field != null) {
            try {
                field.setAccessible(true);
                column = (OColumn) field.get(this);
                if (column.getName() == null)
                    column.setName(field.getName());
                Boolean validField = compatibleField(field);
                if (validField) {
                    // Functional Method
                    Method method = checkForFunctionalColumn(field);
                    if (method != null) {
                        column.setIsFunctionalColumn(true);
                        column.setFunctionalMethod(method);
                        column.setFunctionalStore(checkForFunctionalStore(field));
                        column.setFunctionalStoreDepends(getFunctionalDepends(field));
                        if (!column.canFunctionalStore()) {
                            column.setLocalColumn();
                        }
                    }

                    // Onchange method for column
                    Method onChangeMethod = checkForOnChangeMethod(field);
                    if (onChangeMethod != null) {
                        column.setOnChangeMethod(onChangeMethod);
                        column.setOnChangeBGProcess(checkForOnChangeBGProcess(field));
                    }

                    // domain filter on column
                    column.setHasDomainFilterColumn(isDomainFilterColumn(field));
                    return column;
                }
            } catch (Exception e) {
                e.printStackTrace();
                Log.e(TAG, e.getMessage());
            }
        }
        return null;
    }

    private boolean isDomainFilterColumn(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.hasDomainFilter.class);
        if (annotation != null) {
            Odoo.hasDomainFilter domainFilter = (Odoo.hasDomainFilter) annotation;
            return domainFilter.checkDomainRuntime();
        }
        return false;
    }

    private Boolean checkForOnChangeBGProcess(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.onChange.class);
        if (annotation != null) {
            Odoo.onChange onChange = (Odoo.onChange) annotation;
            return onChange.bg_process();
        }
        return false;
    }

    private Method checkForOnChangeMethod(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.onChange.class);
        if (annotation != null) {
            Odoo.onChange onChange = (Odoo.onChange) annotation;
            String method_name = onChange.method();
            try {
                return getClass().getMethod(method_name, ODataRow.class);
            } catch (NoSuchMethodException e) {
                Log.e(TAG, "No Such Method: " + e.getMessage());
            }
        }
        return null;
    }

    /**
     * Check for functional store.
     *
     * @param field the field
     * @return the boolean
     */
    public Boolean checkForFunctionalStore(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.Functional.class);
        if (annotation != null) {
            Odoo.Functional functional = (Odoo.Functional) annotation;
            return functional.store();
        }
        return false;
    }

    /**
     * Gets the functional depends.
     *
     * @param field the field
     * @return the functional depends
     */
    public String[] getFunctionalDepends(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.Functional.class);
        if (annotation != null) {
            Odoo.Functional functional = (Odoo.Functional) annotation;
            return functional.depends();
        }
        return null;
    }

    private Method checkForFunctionalColumn(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.Functional.class);
        if (annotation != null) {
            Odoo.Functional functional = (Odoo.Functional) annotation;
            String method_name = functional.method();
            try {
                if (functional.store())
                    return getClass().getMethod(method_name, OValues.class);
                else
                    return getClass().getMethod(method_name, ODataRow.class);
            } catch (NoSuchMethodException e) {
                Log.e(TAG, "No Such Method: " + e.getMessage());
            }
        }
        return null;
    }

    private boolean compatibleField(Field field) {
        if (mOdooVersion != null) {
            Annotation[] annotations = field.getDeclaredAnnotations();
            if (annotations.length > 0) {
                int version = 0;
                for (Annotation annotation : annotations) {
                    // Check for odoo api annotation
                    Class<? extends Annotation> type = annotation.annotationType();
                    if (type.getDeclaringClass().isAssignableFrom(Odoo.api.class)) {
                        switch (mOdooVersion.getVersion_number()) {
                        case 9:
                            if (type.isAssignableFrom(Odoo.api.v9alpha.class)) {
                                version++;
                            }
                            break;
                        case 8:
                            if (type.isAssignableFrom(Odoo.api.v8.class)) {
                                version++;
                            }
                            break;
                        case 7:
                            if (type.isAssignableFrom(Odoo.api.v7.class)) {
                                version++;
                            }
                            break;
                        }
                    }
                    // Check for functional annotation
                    if (type.isAssignableFrom(Odoo.Functional.class) || type.isAssignableFrom(Odoo.onChange.class)
                            || type.isAssignableFrom(Odoo.hasDomainFilter.class)) {
                        version++;
                    }
                }
                return (version > 0) ? true : false;
            }
            return true;
        }
        return false;
    }

    private void prepareFields() {
        mColumns.clear();
        mRelationColumns.clear();
        List<Field> fields = new ArrayList<>();
        fields.addAll(Arrays.asList(getClass().getSuperclass().getDeclaredFields()));
        fields.addAll(Arrays.asList(getClass().getDeclaredFields()));
        mDeclaredFields.clear();
        for (Field field : fields) {
            if (field.getType().isAssignableFrom(OColumn.class)) {
                String name = field.getName();
                try {
                    OColumn column = getColumn(field);
                    if (column != null) {
                        name = column.getName();
                        if (column.getRelationType() != null) {
                            mRelationColumns.add(column);
                        }
                        if (column.isFunctionalColumn()) {
                            if (column.canFunctionalStore()) {
                                mColumns.add(column);
                            }
                            mFunctionalColumns.add(column);
                        } else {
                            mColumns.add(column);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                mDeclaredFields.put(name, field);
            }
        }
    }

    public List<OColumn> getFunctionalColumns() {
        if (mColumns.size() <= 0)
            prepareFields();
        return mFunctionalColumns;
    }

    public String getModelName() {
        return model_name;
    }

    public List<OColumn> getManyToManyColumns(OModel relation_model) {
        List<OColumn> cols = new ArrayList<OColumn>();
        _write_date.setName("_write_date");
        cols.add(_write_date);
        _is_dirty.setName("_is_dirty");
        cols.add(_is_dirty);
        _is_active.setName("_is_active");
        cols.add(_is_active);

        OColumn base_id = new OColumn("Base Id", OInteger.class);
        base_id.setName(getTableName() + "_id");
        cols.add(base_id);
        OColumn relation_id = new OColumn("Relation Id", OInteger.class);
        relation_id.setName(relation_model.getTableName() + "_id");
        cols.add(relation_id);
        return cols;
    }

    public OModel createInstance(Class<?> type) {
        try {
            Constructor<?> constructor = type.getConstructor(Context.class, OUser.class);
            return (OModel) constructor.newInstance(mContext, mUser);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getTableName() {
        return getModelName().replaceAll("\\.", "_");
    }

    public String toString() {
        return getModelName();
    }

    public static OModel get(Context context, String model_name, String username) {
        OModel model = modelRegistry.getModel(model_name, username);
        OUser user = OdooAccountManager.getDetails(context, username);
        if (model == null) {
            try {
                OPreferenceManager pfManager = new OPreferenceManager(context);
                Class<?> model_class = Class.forName(pfManager.getString(model_name, null));
                if (model_class != null) {
                    model = new OModel(context, model_name, user).createInstance(model_class);
                    if (model != null) {
                        modelRegistry.register(model);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return model;
    }

    public String authority() {
        return BASE_AUTHORITY;
    }

    public Uri buildURI(String authority) {
        BASE_AUTHORITY = authority;
        String path = getModelName().toLowerCase(Locale.getDefault());
        return BaseModelProvider.buildURI(BASE_AUTHORITY, path, mUser.getAndroidName());
    }

    public Uri uri() {
        String path = getModelName().toLowerCase(Locale.getDefault());
        return BaseModelProvider.buildURI(BASE_AUTHORITY, path, mUser.getAndroidName());
    }

    public ODomain defaultDomain() {
        return new ODomain();
    }

    private String[] updateProjection(String[] projection) {
        HashSet<String> names = new HashSet<>();
        String[] allProjection = projection;
        if (allProjection == null) {
            allProjection = projection();
        } else {
            for (String col : projection) {
                OColumn column = getColumn(col);
                if (column.isFunctionalColumn() && column.canFunctionalStore()) {
                    names.add(column.getName());
                }
            }
        }
        names.addAll(Arrays.asList(allProjection));
        names.addAll(
                Arrays.asList(new String[] { OColumn.ROW_ID, "id", "_write_date", "_is_dirty", "_is_active" }));
        return names.toArray(new String[names.size()]);
    }

    public String[] projection(Boolean onlyServerColumns) {
        List<String> names = new ArrayList<>();
        for (OColumn column : getColumns(false)) {
            if (column.getRelationType() == null || column.canFunctionalStore()) {
                names.add(column.getName());
            } else if (column.getRelationType() == OColumn.RelationType.ManyToOne) {
                names.add(column.getName());
            }
        }
        return names.toArray(new String[names.size()]);
    }

    public String[] projection() {
        List<String> names = new ArrayList<>();
        for (OColumn column : getColumns()) {
            if (column.getRelationType() == null || column.canFunctionalStore()) {
                names.add(column.getName());
            } else if (column.getRelationType() == OColumn.RelationType.ManyToOne) {
                names.add(column.getName());
            }
        }
        return names.toArray(new String[names.size()]);
    }

    // Sync default methods
    public boolean checkForCreateDate() {
        return true;
    }

    public boolean checkForWriteDate() {
        return true;
    }

    public boolean allowUpdateRecordOnServer() {
        return true;
    }

    public boolean allowCreateRecordOnServer() {
        return true;
    }

    public boolean allowDeleteRecordOnServer() {
        return true;
    }

    public boolean allowDeleteRecordInLocal() {
        return true;
    }

    // Database Operations

    public String getLabel(String column, String key) {
        OColumn col = getColumn(column);
        if (col.getType().isAssignableFrom(OSelection.class)) {
            return col.getSelectionMap().get(key);
        }
        return "false";
    }

    public ODataRow browse(int row_id) {
        return browse(null, row_id);
    }

    public ODataRow browse(String[] projection, int row_id) {
        List<ODataRow> rows = select(projection, OColumn.ROW_ID + " = ?", new String[] { row_id + "" });
        if (rows.size() > 0) {
            return rows.get(0);
        }
        return null;
    }

    public ODataRow browse(String[] projection, String selection, String[] args) {
        List<ODataRow> rows = select(updateProjection(projection), selection, args);
        if (rows.size() > 0) {
            return rows.get(0);
        }
        return null;
    }

    public List<Integer> getServerIds() {
        List<Integer> ids = new ArrayList<>();
        for (ODataRow row : select(new String[] { "id" })) {
            if (row.getInt("id") != 0) {
                ids.add(row.getInt("id"));
            }
        }
        return ids;
    }

    public boolean isEmptyTable() {
        return (count(null, null) <= 0);
    }

    public String getLastSyncDateTime() {
        IrModel model = new IrModel(mContext, mUser);
        List<ODataRow> records = model.select(null, "model = ?", new String[] { getModelName() });
        if (records.size() > 0) {
            String date = records.get(0).getString("last_synced");
            Date write_date = ODateUtils.createDateObject(date, ODateUtils.DEFAULT_FORMAT, true);
            Calendar cal = Calendar.getInstance();
            cal.setTime(write_date);
            /*
            Fixed for Postgres SQL
            It stores milliseconds so comparing date wrong. 
             */
            cal.set(Calendar.SECOND, cal.get(Calendar.SECOND) + 2);
            write_date = cal.getTime();
            return ODateUtils.getDate(write_date, ODateUtils.DEFAULT_FORMAT);
        }
        return null;
    }

    public List<ODataRow> select() {
        return select(null, null, null, null);
    }

    public List<ODataRow> select(String[] projection) {
        return select(projection, null, null, null);
    }

    public List<ODataRow> select(String[] projection, String where, String[] args) {
        return select(projection, where, args, null);
    }

    public List<ODataRow> select(String[] projection, String where, String[] args, String sortOrder) {
        Cursor cr = mContext.getContentResolver().query(uri(), updateProjection(projection), where, args,
                sortOrder);
        List<ODataRow> rows = new ArrayList<>();
        try {
            if (cr != null && cr.moveToFirst()) {
                do {
                    ODataRow row = OCursorUtils.toDatarow(cr);
                    for (OColumn column : getRelationColumns(projection)) {
                        if (!row.getString(column.getName()).equals("false")
                                || column.getRelationType() == OColumn.RelationType.OneToMany
                                || column.getRelationType() == OColumn.RelationType.ManyToMany) {
                            switch (column.getRelationType()) {
                            case ManyToMany:
                                OM2MRecord m2mRecords = new OM2MRecord(this, column, row.getInt(OColumn.ROW_ID));
                                row.put(column.getName(), m2mRecords);
                                break;
                            case ManyToOne:
                                OM2ORecord m2ORecord = new OM2ORecord(this, column, row.getInt(column.getName()));
                                row.put(column.getName(), m2ORecord);
                                break;
                            case OneToMany:
                                OO2MRecord o2MRecord = new OO2MRecord(this, column, row.getInt(OColumn.ROW_ID));
                                row.put(column.getName(), o2MRecord);
                                break;
                            }
                        }
                    }
                    for (OColumn column : getFunctionalColumns(projection)) {
                        List<String> depends = column.getFunctionalStoreDepends();
                        if (depends != null && depends.size() > 0) {
                            ODataRow values = new ODataRow();
                            for (String depend : depends) {
                                if (row.contains(depend)) {
                                    values.put(depend, row.get(depend));
                                }
                            }
                            if (values.size() == depends.size()) {
                                Object value = getFunctionalMethodValue(column, values);
                                row.put(column.getName(), value);
                            }
                        }
                    }
                    rows.add(row);
                } while (cr.moveToNext());
            }
        } finally {
            cr.close();
        }
        return rows;
    }

    public Object getFunctionalMethodValue(OColumn column, Object record) {
        if (column.isFunctionalColumn()) {
            Method method = column.getFunctionalMethod();
            OModel model = this;
            try {
                return method.invoke(model, new Object[] { record });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    public Object getOnChangeMethodValue(OColumn column, Object record) {
        Method method = column.getOnChangeMethod();
        OModel model = this;
        try {
            return method.invoke(model, new Object[] { record });
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    private List<OColumn> getFunctionalColumns(String[] projection) {
        List<OColumn> cols = new ArrayList<>();
        if (projection != null) {
            for (String key : projection) {
                OColumn column = getColumn(key);
                if (column.isFunctionalColumn() && !column.canFunctionalStore()) {
                    cols.add(column);
                }
            }
        } else {
            for (OColumn column : getFunctionalColumns()) {
                if (!column.canFunctionalStore())
                    cols.add(column);
            }
        }
        return cols;
    }

    private List<OColumn> getRelationColumns(String[] projection) {
        List<OColumn> cols = new ArrayList<>();
        if (projection != null) {
            for (String key : projection) {
                OColumn column = getColumn(key);
                if (column.getRelationType() != null) {
                    cols.add(column);
                }
            }
        } else {
            cols.addAll(getRelationColumns());
        }
        return cols;
    }

    public int insertOrUpdate(int serverId, OValues values) {
        if (hasServerRecord(serverId)) {
            int row_id = selectRowId(serverId);
            update(row_id, values);
            return row_id;
        } else {
            return insert(values);
        }
    }

    public int insertOrUpdate(String selection, String[] args, OValues values) {
        int count = update(selection, args, values);
        if (count <= 0) {
            return insert(values);
        } else {
            return selectRowId(selection, args);
        }
    }

    public int selectRowId(String selection, String[] args) {
        int row_id = INVALID_ROW_ID;
        SQLiteDatabase db = getReadableDatabase();
        Cursor cr = db.query(getTableName(), new String[] { OColumn.ROW_ID }, selection, args, null, null, null);
        try {
            if (cr.moveToFirst()) {
                row_id = cr.getInt(0);
            }
        } finally {
            cr.close();
        }
        return row_id;
    }

    public int selectServerId(int row_id) {
        return browse(row_id).getInt("id");
    }

    public int selectRowId(int server_id) {
        List<ODataRow> rows = select(new String[] { OColumn.ROW_ID }, "id = ?", new String[] { server_id + "" });
        if (rows.size() > 0) {
            return rows.get(0).getInt(OColumn.ROW_ID);
        }
        return INVALID_ROW_ID;
    }

    public int insert(OValues values) {
        Uri uri = mContext.getContentResolver().insert(uri(), values.toContentValues());
        if (uri != null) {
            return Integer.parseInt(uri.getLastPathSegment());
        }
        return INVALID_ROW_ID;
    }

    public boolean hasServerRecord(int server_id) {
        int count = count("id = ? ", new String[] { server_id + "" });
        return (count > 0);
    }

    public boolean isServerRecordDirty(int server_id) {
        int count = count("id = ? and _is_dirty = ?", new String[] { server_id + "", "true" });
        return (count > 0);
    }

    public boolean hasRecord(int row_id) {
        int count = count(OColumn.ROW_ID + " = ? ", new String[] { row_id + "" });
        return (count > 0);
    }

    public int deleteRecords(List<Integer> serverIds, boolean permanently) {
        String selection = "id IN (" + StringUtils.repeat("?, ", serverIds.size() - 1) + " ?)";
        String[] args = OListUtils.toStringList(serverIds).toArray(new String[serverIds.size()]);
        if (permanently) {
            return delete(selection, args, true);
        } else {
            OValues values = new OValues();
            values.put("_is_active", "false");
            return update(selection, args, values);
        }
    }

    public int delete(String selection, String[] args) {
        return delete(selection, args, false);
    }

    public int delete(String selection, String[] args, boolean permanently) {
        int count = 0;
        if (permanently) {
            count = mContext.getContentResolver().delete(uri(), selection, args);
        } else {
            List<ODataRow> records = select(new String[] { "_is_active" }, selection, args);
            for (ODataRow row : records) {
                if (row.getBoolean("_is_active")) {
                    OValues values = new OValues();
                    values.put("_is_active", "false");
                    update(row.getInt(OColumn.ROW_ID), values);
                }
                count++;
            }
        }
        return count;
    }

    public boolean delete(int row_id) {
        return delete(row_id, false);
    }

    public boolean delete(int row_id, boolean permanently) {
        int count = 0;
        if (permanently)
            count = mContext.getContentResolver().delete(uri().withAppendedPath(uri(), row_id + ""), null, null);
        else {
            OValues values = new OValues();
            values.put("_is_active", "false");
            update(row_id, values);
            count++;
        }
        return (count > 0) ? true : false;
    }

    public int update(String selection, String[] args, OValues values) {
        return mContext.getContentResolver().update(uri(), values.toContentValues(), selection, args);
    }

    public boolean update(int row_id, OValues values) {
        int count = mContext.getContentResolver().update(uri().withAppendedPath(uri(), row_id + ""),
                values.toContentValues(), null, null);
        return (count > 0) ? true : false;
    }

    public List<ODataRow> query(String query) {
        return query(query, null);
    }

    public List<ODataRow> query(String query, String[] args) {
        List<ODataRow> rows = new ArrayList<>();
        SQLiteDatabase db = getReadableDatabase();
        Cursor cr = db.rawQuery(query, args);
        try {
            if (cr.moveToFirst()) {
                do {
                    rows.add(OCursorUtils.toDatarow(cr));
                } while (cr.moveToNext());
            }
        } finally {
            cr.close();
        }
        return rows;

    }

    public int count(String selection, String[] args) {
        int count = 0;
        SQLiteDatabase db = getReadableDatabase();
        Cursor cr = db.query(getTableName(), new String[] { "count(*)" }, selection, args, null, null, null);
        try {
            cr.moveToFirst();
            count = cr.getInt(0);
        } finally {
            cr.close();
        }
        return count;
    }

    public void storeManyToManyRecord(String column_name, int row_id, List<Integer> relationIds, Command command)
            throws InvalidObjectException {
        OColumn column = getColumn(column_name);
        if (column != null) {
            OModel rel_model = createInstance(column.getType());
            String table = getTableName() + "_" + rel_model.getTableName() + "_rel";
            String base_column = getTableName() + "_id";
            String rel_column = rel_model.getTableName() + "_id";

            SQLiteDatabase db = getWritableDatabase();
            try {
                switch (command) {
                case Add:
                    if (relationIds.size() > 0) {
                        for (int id : relationIds) {
                            ContentValues values = new ContentValues();
                            values.put(base_column, row_id);
                            values.put(rel_column, id);
                            values.put("_write_date", ODateUtils.getDate());
                            db.insert(table, null, values);
                        }
                    }
                    break;
                case Update:
                    break;
                case Delete:
                    // Deleting records to relation model
                    if (relationIds.size() > 0) {
                        for (int id : relationIds) {
                            db.delete(table, base_column + " = ? AND  " + rel_column + " = ?",
                                    new String[] { row_id + "", id + "" });
                        }
                    }
                    break;
                case Replace:
                    // Removing old entries
                    db.delete(table, base_column + " = ?", new String[] { row_id + "" });
                    // Creating new entries
                    storeManyToManyRecord(column_name, row_id, relationIds, Command.Add);
                    break;
                }
            } finally {
                db.close();
                rel_model.close();
            }
        } else {
            throw new InvalidObjectException(
                    "Column [" + column_name + "] not found in " + getModelName() + " model.");

        }
    }

    public List<ODataRow> selectManyToManyRecords(String[] projection, String column_name, int row_id) {
        OColumn column = getColumn(column_name);
        OModel rel_model = createInstance(column.getType());
        String table = getTableName() + "_" + rel_model.getTableName() + "_rel";
        String base_column = getTableName() + "_id";
        String rel_column = rel_model.getTableName() + "_id";

        // Getting relation table ids
        List<String> ids = new ArrayList<>();
        SQLiteDatabase db = getReadableDatabase();
        Cursor cr = null;
        try {
            cr = db.query(table, new String[] { rel_column }, base_column + "=?", new String[] { row_id + "" },
                    null, null, null);
            if (cr.moveToFirst()) {
                do {
                    ids.add(cr.getInt(0) + "");
                } while (cr.moveToNext());
            }
        } finally {
            if (cr != null) {
                cr.close();
            }
        }
        List<ODataRow> data = rel_model.select(projection,
                OColumn.ROW_ID + " IN (" + StringUtils.repeat(" ?, ", ids.size() - 1) + " ?)",
                ids.toArray(new String[ids.size()]));
        rel_model.close();
        return data;
    }

    public ServerDataHelper getServerDataHelper() {
        return new ServerDataHelper(mContext, this, getUser());
    }

    public String getName(int row_id) {
        ODataRow row = browse(row_id);
        if (row != null) {
            return row.getString("name");
        }
        return "false";
    }

    public void quickSyncRecords(ODomain domain) {
        OSyncAdapter syncAdapter = new OSyncAdapter(mContext, getClass(), null, true);
        syncAdapter.setModel(this);
        syncAdapter.setDomain(domain);
        syncAdapter.checkForWriteCreateDate(false);
        syncAdapter.onPerformSync(getUser().getAccount(), null, authority(), null, new SyncResult());
    }

    public ODataRow quickCreateRecord(ODataRow record) {
        OSyncAdapter syncAdapter = new OSyncAdapter(mContext, getClass(), null, true);
        syncAdapter.setModel(this);
        ODomain domain = new ODomain();
        domain.add("id", "=", record.getInt("id"));
        syncAdapter.setDomain(domain);
        syncAdapter.checkForWriteCreateDate(false);
        syncAdapter.onPerformSync(getUser().getAccount(), null, authority(), null, new SyncResult());
        return browse(null, "id = ?", new String[] { record.getString("id") });
    }

    public ODataRow countGroupBy(String column, String group_by, String having, String[] args) {
        String sql = "select count(*) as total, " + column;
        sql += " from " + getTableName() + " group by " + group_by + " having " + having;
        List<ODataRow> data = query(sql, args);
        if (data.size() > 0) {
            return data.get(0);
        } else {
            ODataRow row = new ODataRow();
            row.put("total", 0);
            return row;
        }
    }

    public boolean isInstalledOnServer(String module_name) {
        try {
            App app = (App) mContext.getApplicationContext();
            IrModel model = new IrModel(mContext, getUser());
            List<ODataRow> modules = model.select(null, "name = ?", new String[] { module_name.trim() });
            if (modules.size() > 0) {
                if (modules.get(0).getString("state").equals("installed")) {
                    return true;
                }
            }
            if (app.inNetwork()) {
                odoo.Odoo odoo = app.getOdoo(getUser());
                OdooFields fields = new OdooFields(new String[] { "state", "name" });
                ODomain domain = new ODomain();
                domain.add("name", "=", module_name);
                JSONArray result = odoo.search_read("ir.module.module", fields.get(), domain.get())
                        .getJSONArray("records");
                if (result.length() > 0) {
                    JSONObject record = result.getJSONObject(0);
                    if (record.getString("state").equals("installed")) {
                        OValues values = new OValues();
                        values.put("id", record.getInt("id"));
                        values.put("name", record.getString("name"));
                        values.put("state", record.getString("state"));
                        model.insertOrUpdate(record.getInt("id"), values);
                        return true;
                    }
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public String getDatabaseLocalPath() {
        return sqLite.databaseLocalPath();
    }

    public void exportDB() {
        FileChannel source;
        FileChannel destination;
        String currentDBPath = getDatabaseLocalPath();
        String backupDBPath = OStorageUtils.getDirectoryPath("file") + "/" + getDatabaseName();
        File currentDB = new File(currentDBPath);
        File backupDB = new File(backupDBPath);
        try {
            source = new FileInputStream(currentDB).getChannel();
            destination = new FileOutputStream(backupDB).getChannel();
            destination.transferFrom(source, 0, source.size());
            source.close();
            destination.close();
            String subject = "Database Export: " + getDatabaseName();
            Uri uri = Uri.fromFile(backupDB);
            Intent intent = new Intent(Intent.ACTION_SEND);
            intent.putExtra(Intent.EXTRA_STREAM, uri);
            intent.putExtra(Intent.EXTRA_SUBJECT, subject);
            intent.setType("message/rfc822");
            mContext.startActivity(intent);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onSyncStarted() {
        // Will be over ride by extending model
    }

    @Override
    public void onSyncFinished() {
        // Will be over ride by extending model
    }
}