ionic 访问odoo11之具体业务类api接口
在前面测试通过odoo登录的功能,这次的问题重点是如何访问后台具体的业务类的接口呢?这次就以我们在odoo中安装的lunch模块为例,目标是获取lunch.alert的数据,如下图

具体过程接上次文章,继续完善OdooJsonRpc类的代码,首先是基础代码,这个是需要提供具体的model名称和具体方法,也是是一个很基础的方法,后台的odoo网站会利用类似C#反射的机制调用目标类的方法。
/** * Calls the method of that particular model * @param model Model name * @param method Method name of particular model * @param args Array of fields * @param kwargs Object */ public call(model: string, method: string, args: any, kwargs?: any) { kwargs = kwargs || {}; let params = { model: model, method: method, args: args, kwargs: kwargs == false ? {} : kwargs, context: this.getContext() }; return this.sendRequest("/web/dataset/call_kw", params); }
调用以上基础call方法的几个基本封装函数有如下几个,基本实现了对数据的CRUD功能,当然基本上是针对一条数据的操作
/** * Reads that perticular fields of that particular ID * @param model Model Name * @param id Id of that record which you want to read * @param mArgs Array of fields which you want to read of the particular id */ public read(model: string, id: number, mArgs: any): Promise<any> { let args = [ id, [mArgs] ] return this.call(model, 'read', args) } /** * Provide the name that you want to search * @param model Model name * @param name Name that you want to search */ public nameSearch(model: string, name: string): Promise<any> { let kwargs = { name: name, args: [], operator: "ilike", limit: 0 } return this.call(model, 'name_search', [], kwargs) } /** * Provide the IDs and you will get the names of that paticular IDs * @param model Model name * @param mArgs Array of IDs that you want to pass */ public nameGet(model: string, mArgs: any): Promise<any> { let args = [mArgs] return this.call(model, 'name_get', args) } /** * Create a new record * @param model Model name * @param mArgs Object of fields and value */ public createRecord(model: string, mArgs: any) { let args = [mArgs]; return this.call(model, "create", args, null) } /** * Delete the record of particular ID * @param model Model Name * @param id Id of record that you want to delete */ public deleteRecord(model: string, id: number) { let mArgs = [id] return this.call(model, "unlink", mArgs, null) } /** * Updates the record of particular ID * @param model Model Name * @param id Id of record that you want to update the. * @param mArgs The Object of fields and value that you want to update * (e.g) * let args = { * "name": "Mtfa" * } */ public updateRecord(model: string, id: number, mArgs: any) { let args = [ [id], mArgs ] return this.call(model, "write", args, null) }
针对单条数据的读取,这里还有另外一种方法,类似上面的read函数
/** * Loads all data of the paricular ID * @param model Model name * @param id Id of that particular data which you want to load */ public load(model: string, id: number): Promise<any> { let params = { model: model, id: id, fields: [], context: this.getContext() } return this.sendRequest("/web/dataset/load", params) }
还有对odoo多条件查询的情况,尤其还有分页的问题,这里也有这样一个很实用的分页查询的方法,这个在日后app的开发中会经常用到
/** * Fires query in particular model with fields and conditions * @param model Model name * @param domain Conditions that you want to fire on your query * (e.g) let domain = [ * ["id","=",11] * ] * @param fields Fields names which you want to bring from server * (e.g) let fields = [ * ["id","name","email"] * ] * @param limit limit of the record * @param offset * @param sort sorting order of data (e.g) let sort = "ascending" */ public searchRead(model: string, domain: any, fields: any, limit: number, offset: any, sort: string) { let params = { model: model, fields: fields, domain: domain, offset: offset, limit: limit, sort: sort, context: this.getContext() }; return this.sendRequest("/web/dataset/search_read", params); }
到此,访问odoo具体业务类的OdooJsonRpc类封装完毕,完整的代码在这里。接下来怎么获取我们的lunch.alert数据呢??
首先定义我们访问odoo业务类的成员变量,这里我们的model类是lunch.alert,需要获取的字段是message和id,其他参数我们暂使用默认的值
private lunchAlert = "lunch.alert"; private fields = ["message", "id"]; private domain = [] private sort = "" private limit = 0 private offset = 0 private items: Array<{ id: number, message: string }> = []
第二步是,与odoo后台网站交互获取数据的过程
private TestMyOdooModel() { this.odooRpc.searchRead(this.lunchAlert,this.domain, this.fields, this.limit, this.offset,this.sort) .then((lunchAlerts: any) => { let json = JSON.parse(lunchAlerts._body); if (!json.error) { let query = json["result"].records for (let i in query) { this.items.push ({ id: query[i].id, message: query[i].message }) } this.utils.presentAlert("Lunch Message", this.items[0].message,[{text: "Ok"}]) } else { this.utils.presentAlert("LunchAlert", "Parse lunch alert error",[{text: "Ok"}]) } }) }
如果正确获取数据,我们会弹出第一条数据对应的message.
编译打包成*.apk,在手机上测试,确实成功了,如下图所示。

由于这些都是测试阶段的代码,将来可能会改变,现阶段把这些代码全部放在这里。
测试部分的Page页面odooLogin.ts
import { Component } from '@angular/core';
import {
AlertController,
IonicPage,
Loading,
LoadingController,
NavController,
NavParams
} from 'ionic-angular';
import { OdooJsonRpc } from '../../../../providers/baseService/Odoojsonrpc';
import { Utils } from "../../../../providers/baseService/Utils";
@IonicPage()
@Component
({
selector: 'page-odooLogin',
templateUrl: 'odooLogin.html',
})
export class OdooLoginPage
{
private listForProtocol: Array<{ protocol: string}> = []
public perfectUrl: boolean = false
public odooUrl
public selectedProtocol
private dbList: Array<{ dbName: string}> = []
private selectedDatabase
private email
private password
constructor(public navCtrl: NavController,
private alert: AlertController, public navParams: NavParams,
private odooRpc: OdooJsonRpc, private loadingCtrl: LoadingController,
private utils: Utils)
{
this.listForProtocol.push({ protocol: "http" })
this.listForProtocol.push({protocol: "https"})
}
public checkUrl()
{
this.utils.presentLoading("Please Wait")
this.odooRpc.init
({
odoo_server: this.selectedProtocol + "://" + this.odooUrl
//http_auth: 'username:password' // optional
})
this.odooRpc.getDbList().then((dbList: any) =>
{
console.log(dbList)
this.perfectUrl = true
this.utils.dismissLoading()
this.fillData(dbList)
}).
catch((err: any) =>
{
console.log(err)
this.utils.presentAlert("Error", "You Entered a wrong Odoo URL",
[{
text: "Ok"
}])
this.utils.dismissLoading()
});
}
public fillData(res: any)
{
let body = JSON.parse(res._body)
let json = body['result'];
this.dbList.length = 0;
for (var key in json)
{
this.dbList.push({ dbName: json[key] });
}
}
private login()
{
this.utils.presentLoading("Please wait", 0, true)
this.odooRpc.login(this.selectedDatabase, this.email, this.password)
.then((res: any) =>
{
let logiData: any = JSON.parse(res._body)["result"];
logiData.password = this.password
localStorage.setItem("token", JSON.stringify(logiData));
//this.utils.dismissLoading()
this.utils.presentAlert("Congratulation", "You login success",[{text: "Ok"}])
this.TestMyOdooModel()
}).
catch((err) =>
{
this.utils.presentAlert("Error", "Username or password must be incorrect",
[{
text: "Ok"
}])
});
}
private lunchAlert = "lunch.alert";
private fields = ["message", "id"];
private domain = []
private sort = ""
private limit = 0
private offset = 0
private items: Array<{ id: number, message: string }> = []
private TestMyOdooModel()
{
this.odooRpc.searchRead(this.lunchAlert,this.domain, this.fields, this.limit, this.offset,this.sort)
.then((lunchAlerts: any) =>
{
let json = JSON.parse(lunchAlerts._body);
if (!json.error)
{
let query = json["result"].records
for (let i in query)
{
this.items.push
({
id: query[i].id,
message: query[i].message
})
}
this.utils.presentAlert("Lunch Message", this.items[0].message,[{text: "Ok"}])
}
else
{
this.utils.presentAlert("LunchAlert", "Parse lunch alert error",[{text: "Ok"}])
}
})
}
}
页面布局部分odooLogin.html
<ion-content class="background"> <ion-card> <ion-card-content> <div class="spacer" style="height: 10px;"></div> <ion-item> <ion-label style="color: #fff">Select Protocol</ion-label> <ion-select [(ngModel)]="selectedProtocol" style="color: #fff" name="dbNames"> <ion-option *ngFor="let item of listForProtocol" value="{{item.protocol}}">{{item.protocol}}</ion-option> </ion-select> </ion-item> <div class="spacer" style="height: 10px;"></div> <ion-item> <ion-input [(ngModel)]="odooUrl" type="url" name="odooUrl" placeholder="Odoo Url"></ion-input> </ion-item> <div class="spacer" style="height: 10px;"></div> <button ion-button block round outline color="light" (click)="checkUrl()" text-center> Check Url <ion-icon name="md-arrow-round-forward"></ion-icon> </button> <div [hidden]="!perfectUrl"> <form (ngSubmit)="login()" #registerForm="ngForm"> <div class="spacer" style="height: 10px;"></div> <ion-item> <ion-input type="email" [(ngModel)]="email" name="email" placeholder="Email" required></ion-input> </ion-item> <div class="spacer" style="height: 5px;"></div> <ion-item> <ion-input type="password" [(ngModel)]="password" name="pass" placeholder="Password" required></ion-input> </ion-item> <div class="spacer" style="height: 10px;"></div> <div class="spacer" style="height: 10px;"></div> <ion-item> <ion-label style="color: #fff">Select Database</ion-label> <ion-select [(ngModel)]="selectedDatabase" name="selectDatabase" style="color: #fff" required> <ion-option *ngFor="let item of dbList" value="{{item.dbName}}">{{item.dbName}}</ion-option> </ion-select> </ion-item> <button ion-button block round outline color="light" [disabled]="!registerForm.form.valid" (click)="signin()">Login</button> </form> </div> </ion-card-content> </ion-card> </ion-content
CSS效果部分odooLogin.scss
page-odooLogin { .background { height: 100%; width: 100%; background-size: cover !important; background-position: center center !important; background-image: url('../assets/imgs/mountain.jpg') } ion-card.card { margin-top: 30%; box-shadow: none; background: rgba(0, 0, 0, 0.5); border-radius: 5px; } a, p, ion-card-header.card-header { color: #fff!important; } .list > .item-block:first-child { border: medium none; } .item { margin-bottom: 10px; background: rgba(255, 255, 255, 0.5); border: medium none; .text-input, { color: #fff; } input::-moz-placeholder{ color: #fff!important; } input:-moz-placeholder { color: #fff!important; } *:-moz-placeholder{ color: #fff!important; } *:-ms-input-placeholder{ color: #fff!important; } *::-webkit-input-placeholder{ color: #fff!important; } } }
OdooJsonRpc部分Odoojsonrpc.ts
import { Injectable } from '@angular/core';
import 'rxjs/add/operator/toPromise';
import 'rxjs/Rx';
import { Headers, Http } from '@angular/http';
import { Utils } from './Utils';
@Injectable()
export class OdooJsonRpc
{
private jsonRpcID: number = 0;
private headers: Headers;
private odoo_server: string;
private http_auth: string;
private list = "/web/database/list";
private get_list = "/web/database/get_list";
private jsonrpc = "/jsonrpc";
constructor(private http: Http, private utils: Utils)
{
this.http = http;
}
/**
* Builds a request for odoo server
* @param url Odoo Server URL
* @param params Object
*/
private buildRequest(url: String, params: any)
{
this.jsonRpcID += 1;
return JSON.stringify
({
jsonrpc: "2.0",
method: "call",
id: this.jsonRpcID,
params: params,
});
}
/**
* Returns the error message
* @param response Error response from server
*/
public handleOdooErrors(response: any)
{
let err: string = response.error.data.message
let msg = err.split("\n")
let errMsg = msg[0]
this.utils.presentAlert("Error", errMsg, [{
text: "Ok",
role: "cancel"
}])
}
/**
* Handles HTTP errors
*/
public handleHttpErrors(error: any)
{
return Promise.reject(error.message || error);
}
/**
* Sends a JSON request to the odoo server
* @param url Url of odoo
* @param params Object
*/
public sendRequest(url: string, params: Object): Promise<any>
{
let options = this.buildRequest(url, params);
this.headers = new Headers({
'Content-Type': 'application/json; charset=utf-8',
});
let result = this.http.post(this.odoo_server + url, options, { headers: this.headers })
.toPromise()
return result;
}
public init(configs: any)
{
this.odoo_server = configs.odoo_server;
this.http_auth = configs.http_auth || null;
}
public setOdooServer(odoo_server: string)
{
this.odoo_server = odoo_server;
}
public setHttpAuth(http_auth: string)
{
this.http_auth = http_auth;
}
/**
* Gets the server info
*/
public getServerInfo()
{
return this.sendRequest("/web/webclient/version_info", {});
}
/**
* Gets the session info
*/
public getSessionInfo()
{
return this.sendRequest("/web/session/get_session_info", {});
}
/**
* Gets the Odoo Server Version Number
*/
public getServerVersionNumber(): Promise<number>
{
return this.getServerInfo().then((res: any): Promise<number> =>
{
return new Promise<number>((resolve) =>
{
resolve(JSON.parse(res._body)["result"]["server_version_info"][0]);
});
});
}
/**
* Get the database list
*/
public getDbList(): Promise<string>
{
let dbParams =
{
context: {}
}
return this.getServerVersionNumber().then((data: number) =>
{
if (data <= 8)
{
return this.sendRequest(this.get_list, dbParams);
}
else if (data == 9)
{
return this.sendRequest(this.jsonrpc, dbParams);
}
else
{
return this.sendRequest(this.list, dbParams);
}
})
}
/**
* Returns all modules that are installed in your database
*/
public modules(): Promise<string>
{
let params =
{
context: {}
}
return this.sendRequest("/web/session/modules", params)
}
/**
* Login to the database
* @param db Database name of odoo
* @param login Username
* @param password password
*/
public login(db: string, login: string, password: string)
{
let params =
{
db: db,
login: login,
password: password,
base_location: this.odoo_server,
context: {}
};
return this.sendRequest("/web/session/authenticate", params)
}
/**
* Check whether the session is live or not
*/
public check(): Promise<string>
{
let params =
{
context: this.getContext()
}
return this.sendRequest("/web/session/check", params)
}
/**
* Destroy the session
*/
public destroy()
{
let params =
{
context: {}
}
return this.sendRequest("/web/session/destroy", params)
}
/**
* Fires query in particular model with fields and conditions
* @param model Model name
* @param domain Conditions that you want to fire on your query
* (e.g) let domain = [
* ["id","=",11]
* ]
* @param fields Fields names which you want to bring from server
* (e.g) let fields = [
* ["id","name","email"]
* ]
* @param limit limit of the record
* @param offset
* @param sort sorting order of data (e.g) let sort = "ascending"
*/
public searchRead(model: string, domain: any, fields: any, limit: number, offset: any, sort: string)
{
let params =
{
model: model,
fields: fields,
domain: domain,
offset: offset,
limit: limit,
sort: sort,
context: this.getContext()
};
return this.sendRequest("/web/dataset/search_read", params);
}
/**
* Calls the method of that particular model
* @param model Model name
* @param method Method name of particular model
* @param args Array of fields
* @param kwargs Object
*/
public call(model: string, method: string, args: any, kwargs?: any)
{
kwargs = kwargs || {};
let params =
{
model: model,
method: method,
args: args,
kwargs: kwargs == false ? {} : kwargs,
context: this.getContext()
};
return this.sendRequest("/web/dataset/call_kw", params);
}
/**
* Reads that perticular fields of that particular ID
* @param model Model Name
* @param id Id of that record which you want to read
* @param mArgs Array of fields which you want to read of the particular id
*/
public read(model: string, id: number, mArgs: any): Promise<any>
{
let args =
[
id, [mArgs]
]
return this.call(model, 'read', args)
}
/**
* Loads all data of the paricular ID
* @param model Model name
* @param id Id of that particular data which you want to load
*/
public load(model: string, id: number): Promise<any>
{
let params =
{
model: model,
id: id,
fields: [],
context: this.getContext()
}
return this.sendRequest("/web/dataset/load", params)
}
/**
* Provide the name that you want to search
* @param model Model name
* @param name Name that you want to search
*/
public nameSearch(model: string, name: string): Promise<any>
{
let kwargs =
{
name: name,
args: [],
operator: "ilike",
limit: 0
}
return this.call(model, 'name_search', [], kwargs)
}
/**
* Provide the IDs and you will get the names of that paticular IDs
* @param model Model name
* @param mArgs Array of IDs that you want to pass
*/
public nameGet(model: string, mArgs: any): Promise<any>
{
let args = [mArgs]
return this.call(model, 'name_get', args)
}
/**
* Create a new record
* @param model Model name
* @param mArgs Object of fields and value
*/
public createRecord(model: string, mArgs: any)
{
let args = [mArgs];
return this.call(model, "create", args, null)
}
/**
* Delete the record of particular ID
* @param model Model Name
* @param id Id of record that you want to delete
*/
public deleteRecord(model: string, id: number)
{
let mArgs = [id]
return this.call(model, "unlink", mArgs, null)
}
/**
* Updates the record of particular ID
* @param model Model Name
* @param id Id of record that you want to update the.
* @param mArgs The Object of fields and value that you want to update
* (e.g)
* let args = {
* "name": "Mtfa"
* }
*/
public updateRecord(model: string, id: number, mArgs: any)
{
let args =
[
[id], mArgs
]
return this.call(model, "write", args, null)
}
/**
* Get the User Context from the response of odoo server
*/
private getContext()
{
let response = localStorage.getItem("token");
let jsonData = JSON.parse(response);
let context = jsonData["user_context"];
return context;
}
}
通用类Utils.ts
import { Injectable } from "@angular/core";
import
{
AlertController, Loading,
LoadingController, Toast, ToastController,
ActionSheetController
} from "ionic-angular";
@Injectable()
export class Utils
{
private loading: Loading
constructor(private alrtCtrl: AlertController,
private loadingCtrl: LoadingController,
private toastCtrl: ToastController,
private actionSheetCtrl: ActionSheetController)
{
}
public presentAlert(title: string,
message: string,
buttons: [{}],
subtitle?: string,
enableBackdropDismiss?: boolean,
inputs?: [{}]): void
{
let alrt = this.alrtCtrl.create
({
title: title,
subTitle: subtitle,
message: message,
buttons: buttons,
enableBackdropDismiss: enableBackdropDismiss,
inputs: inputs
})
alrt.present()
}
public presentToast(message: string, duration?: number,
dissmissOnPageChange?: boolean,
position?: string,
showCloseButton?: boolean,
closeButtonText?: string): void
{
let toast = this.toastCtrl.create
({
message: message,
position: position,
dismissOnPageChange: dissmissOnPageChange,
duration: duration,
showCloseButton: showCloseButton,
closeButtonText: closeButtonText
})
toast.present()
}
public presentLoading(content: string, duration?: number,
dissmissOnPageChange?: boolean,
enableBackDropDismiss?: boolean,
showBackDrop?: boolean,
spinner?: string): void
{
this.loading = this.loadingCtrl.create
({
content: content,
dismissOnPageChange: dissmissOnPageChange,
duration: duration,
enableBackdropDismiss: enableBackDropDismiss,
showBackdrop: showBackDrop,
spinner: spinner
})
this.loading.present()
}
public dismissLoading(): void
{
this.loading.dismiss()
}
public presentActionSheet(buttons: [{}], title: string, subtitle?: string,
enableBackdropDismiss?: boolean): void
{
let actionCtrl = this.actionSheetCtrl.create
({
buttons: buttons,
subTitle: subtitle,
title: title,
enableBackdropDismiss: enableBackdropDismiss
})
actionCtrl.present()
}
}
浙公网安备 33010602011771号