在前面测试通过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:
}
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 =
private offset =
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[].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 = ;
for (var key in json)
{
this.dbList.push({ dbName: json[key] });
}
} private login()
{
this.utils.presentLoading("Please wait", , 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 =
private offset =
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[].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: %;
width: %;
background-size: cover !important;
background-position: center center !important;
background-image: url('../assets/imgs/mountain.jpg')
} ion-card.card {
margin-top: %;
box-shadow: none;
background: rgba(, , , 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(, , , 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 = ;
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 += ;
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[] 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"][]);
});
});
} /**
* Get the database list
*/
public getDbList(): Promise<string>
{
let dbParams =
{
context: {}
}
return this.getServerVersionNumber().then((data: number) =>
{
if (data <= )
{
return this.sendRequest(this.get_list, dbParams);
}
else if (data == )
{
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:
}
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()
}
}