Directory structure
| README.md
| __init__.py
| __manifest__.py
+---controllers
| controllers.py
| __init__.py
+---data
| ir_bills.xml
+---models
| order.py
| __init__.py
+---static
| \---src
| +---img
| | default_image.png
| +---js
| | export_xls.js
| \---xml
| export_orders.xml
+---views
| order.xml
| templates.xml
\---wizard
import_wizard.py
import_views.xml
__init__.py
Export Excel File
oders.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record model="ir.ui.view" id="export_order_tree_view">
<field name="name">export_order_tree_view</field>
<field name="model">l.order</field>
<field name="arch" type="xml">
<tree create="0" edit="0" delete="0" import="0" default_order="create_date desc">
<field name="name"/>
<field name="create_date" string="Order Create Date"/>
<field name="customer_id"/>
<field name="own_user_id"/>
<field name="phone" invisible="1"/>
<field name="card_no" invisible="1"/>
<field name="data_type"/>
<field name="stage_qty"/>
<field name="price"/>
<field name="service_charge_rate" widget="percentage"/>
<field name="service_charge"/>
<field name="state" string="Order State"/>
<field name="approval_state"/>
<field name="note"/>
</tree>
</field>
</record>
<record model="ir.actions.act_window" id="export_view_action">
<field name="name">Billing Details</field>
<field name="res_model">j.order</field>
<field name="view_type">form</field>
<field name="view_mode">tree</field>
<field name="group_id" eval="[(4, ref('module.group_manager'))]"/>
<field name="search_view_id" ref="order_search_view"/>
<field name="context">{'search_default_approval_state_2':1}</field>
<field name="view_ids"
eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('export_order_tree_view')})
]"/>
</record>
<menuitem id="export_order_menu" name="Billing Details" parent="main_menu"
action="export_view_action" sequence="13"
groups="module.group_boss"/>
</data>
</odoo>
export_orders.xml
<?xml version="1.0" encoding="utf-8"?>
<templates>
<div t-name="express_extra_buttons" class=" express_extra_buttons">
<button t-if='widget.model=="j.order" and widget.ViewManager.default_view.fields_view.name=="export_order_tree_view"'
type="button" class="btn btn-sm btn-primary export_xls">
Export Data
</button>
</div>
<t t-extend="ListView.buttons">
<t t-jquery="div.o_list_buttons" t-operation="append">
<t t-call="express_extra_buttons"/>
</t>
</t>
<t t-name="PercentageWidgetHide" t-extend="FieldChar"/>
</templates>
export_xls.js
odoo.define('export_module.export_xls', function (require) {
'use strict';
var ListView = require('web.ListView');
var Model = require('web.Model');
var framework = require('web.framework');
var crash_manager = require('web.crash_manager');
ListView.include({
render_buttons: function () {
this._super.apply(this, arguments);
var self = this;
// Export data handle
this.$buttons.on('click', 'button.export_xls', function () {
var export_columns_string = []; // The field name when export
var export_columns_names = []; // The field data when export
for (var i = 0; i < self.columns.length; i++) {
export_columns_string.push(self.columns[i].string);
export_columns_names.push(self.columns[i].name)
}
var model = new Model(self.model);
var list_ids = self.groups.get_selection().ids;
// According to the field query data
model.query(export_columns_names).filter([['id', 'in', list_ids]]).all().then(function (r) {
if (r.length > 0) {
var export_rows = [];
for (var i = 0; i < r.length; i++) {
var rows = []
for (var j = 0; j < export_columns_names.length; j++) {
rows.push(r[i][export_columns_names[j]])
}
export_rows.push(rows)
}
}
// Call controllers to download data
// console.log(export_columns_string, JSON.stringify(export_rows))
console.log(export_columns_string, export_rows);
framework.blockUI();
self.session.get_file({
url: '/export_module/export/xls_data',
data: {
data: JSON.stringify({
model: self.model,
headers: export_columns_string,
rows: export_rows
})
},
complete: framework.unblockUI,
error: crash_manager.rpc_error.bind(crash_manager)
});
});
});
}
});
});
templates.xml
<odoo>
<data>
<template id="assets_backend" name="export_module assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/export_module/static/src/js/export_xls.js"></script>
<link rel="stylesheet" href="/export_module/static/src/export_module.less"/>
</xpath>
</template>
</data>
</odoo>
controllers.py
# -*- coding: utf-8 -*-
import json
import xlwt
import operator
from odoo import http
from odoo.http import request
from odoo.addons.web.controllers.main import serialize_exception
from odoo.addons.web.controllers.main import CSVExport, content_disposition, Export, ExcelExport
try:
from cStringIO import StringIO
except ImportError:
import cStringIO as StringIO
class ExcelExportView(ExcelExport):
def __getattribute__(self, name):
if name == 'fmt':
raise AttributeError()
return super(ExcelExportView, self).__getattribute__(name)
def rows_data_process(self, rows_data):
if rows_data:
for row in rows_data:
for k, v in enumerate(row):
if isinstance(v, list):
row[k] = v[1]
return rows_data
return []
@http.route('/export_module/export/xls_data', type='http', auth='user')
def export_xls_data(self, data, token):
data = json.loads(data)
model = data.get('model', [])
columns_headers = data.get('headers', [])
rows = self.rows_data_process(data.get('rows'))
return request.make_response(
self.from_data(columns_headers, rows),
headers=[
('Content-Disposition', 'attachment; filename="%s"'
% self.filename(model)),
('Content-Type', self.content_type)
],
cookies={'fileToken': token}
)
class Controller(http.Controller):
@http.route('/export_module/export/template', type='http', auth="user")
@serialize_exception
def download_template(self, data, token):
params = json.loads(data)
model, fields = operator.itemgetter('model', 'fields')(params)
columns_headers = [val['label'].strip() for val in fields]
return request.make_response(self.from_data(columns_headers),
headers=[('Content-Disposition',
http.content_disposition(model + '.xls')),
('Content-Type', 'application/vnd.ms-excel')],
cookies={'fileToken': token})
def from_data(self, fields):
workbook = xlwt.Workbook()
worksheet = workbook.add_sheet('Sheet 1')
for i, fieldname in enumerate(fields):
worksheet.write(0, i, fieldname)
worksheet.col(i).width = 4000
fp = StringIO()
workbook.save(fp)
fp.seek(0)
data = fp.read()
fp.close()
return data
Import CSV File
import_wizard.py
# -*- coding: utf-8 -*-
import os
import csv
import base64
import logging
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
from odoo import api, fields, models, tools, _
from odoo.exceptions import except_orm
_logger = logging.getLogger(__name__)
class AlertInfo(except_orm):
pass
class ImportWizard(models.TransientModel):
"""
Import customer bills and change orders approval state
"""
_name = 'import.wizard'
data = fields.Binary(string=u'文件', required=True)
filename = fields.Char(string=u'文件名称', required=True)
@api.multi
def import_bills(self):
this = self[0]
fileformat = os.path.splitext(this.filename)[-1][1:].lower()
if fileformat != 'csv':
error = 'CSV files only'
raise AlertInfo(_("Failed"), error)
try:
self._cr.execute('SAVEPOINT ue_import')
new_data = list(self._read_csv())
_logger.info('start import')
self._read_bills(new_data)
except Exception as e:
_logger.exception('Import Error')
self._cr.execute('ROLLBACK TO SAVEPOINT ue_import')
_logger.info('roll back ue import')
raise AlertInfo(_("Failed"), tools.ustr(e))
else:
self._cr.execute('RELEASE SAVEPOINT ue_import')
_logger.info("import done")
return {
'name': _('Success'),
'view_type': 'form',
'view_mode': 'form',
'view_id': self.env.ref('module_name.import_form_view_success').id,
'res_model': 'import.wizard',
'domain': [],
'context': dict(self._context, active_ids=self.ids),
'type': 'ir.actions.act_window',
'target': 'new',
'res_id': self.id,
}
def _read_csv(self):
csv_data = base64.decodestring(self.data)
try:
csv_data = csv_data.decode('utf-8')
except UnicodeDecodeError:
csv_data = csv_data.decode('gbk')
csv_data = csv_data.encode('utf-8')
csv_iterator = csv.reader(StringIO(csv_data))
csv_u_iter = ([item.decode('utf-8') for item in row] for row in csv_iterator if
any(x for x in row if x.strip()))
try:
header = next(csv_u_iter)
except StopIteration:
return
else:
for line in csv_u_iter:
yield dict(zip(header, line))
def _read_bills(self, new_data):
for data in new_data:
import_name = data.get(u'订单号', '')
customer_order_obj = self.env['customer.order'].search([('name', '=', import_name)])
if customer_order_obj.state == 'done':
approval_state = data.get(u'审批状态')
if approval_state == u'成功' or approval_state == '1':
selection = '1'
else:
selection = '0'
customer_order_obj.write({'approval_state': selection})
import_views.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="import_form_view" model="ir.ui.view">
<field name="name">Import Bills Change Status</field>
<field name="model">import.wizard</field>
<field name="arch" type="xml">
<form>
<div>
<div>
<div>注意:</div>
<div style="margin-left:20px">1.下载模板,并按模板数据格式填写数据.</div>
<div style="margin-left:20px">2.不允许修改模板文件的后缀名以及标题.</div>
</div>
</div>
<div style="margin-top:50px">
<a href='/module_name/download_models/template_csv' target='_blank'>下载模板</a>
</div>
<group>
<group>
<field name="data" filename="filename"/>
<field name="filename" invisible="1"/>
</group>
</group>
<footer>
<button name="import_bills" string="导入" type="object" class="btn-primary"/>
<button string="取消" class="btn-default" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="bills_import_action" model="ir.actions.act_window">
<field name="name">Import Bills</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">import.wizard</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="import_form_view"/>
<field name="target">new</field>
</record>
<menuitem id="import_bills_menu" name="Import Bills" parent="module_name.main_menu"
action="bills_import_action" sequence="15"
groups="module_name.group_boss"/>
<record id="import_form_view_success" model="ir.ui.view">
<field name="name">Import Bills Change Status Success</field>
<field name="model">import.wizard</field>
<field name="arch" type="xml">
<form>
<group colspan="4">
<label string="导入成功"/>
</group>
<footer>
<button special="cancel" string="关闭" class="btn-primary"/>
</footer>
</form>
</field>
</record>
</data>
</odoo>
ir_bills.xml
<odoo>
<data>
<record id="import_bills_template" model="ir.exports">
<field name="name">Results the template for approval of the bills</field>
<field name="resource">customer.order</field>
</record>
<record id="import_bill_field_1" model="ir.exports.line">
<field name="name">name</field>
<field name="export_id" ref="import_bills_template"/>
</record>
<record id="import_bill_field_2" model="ir.exports.line">
<field name="name">customer_id</field>
<field name="export_id" ref="import_bills_template"/>
</record>
<record id="import_bill_field_3" model="ir.exports.line">
<field name="name">approval_state</field>
<field name="export_id" ref="import_bills_template"/>
</record>
</data>
</odoo>
controllers.py
# -*- coding: utf-8 -*-
from odoo.http import request
from odoo import http
from odoo.addons.web.controllers.main import CSVExport, content_disposition, Export, ExcelExport
class DownloadModels(http.Controller):
@http.route('/module_name/download_models/template_csv', auth='public', csrf=False)
def bill_template_csv(self):
"""
Download customer bill template file
"""
records = request.env['j.order'].search([], limit=3)
name_list = Export().namelist('j.order', request.env.ref('module_name.import_bills_template').sudo().id)
field_names = [i['name'] for i in name_list]
field_labels = [i['label'] for i in name_list]
data = records.export_data(field_names).get('datas', [])
d = CSVExport().from_data(field_labels, data)
return http.Response(d, headers=[('Content-Disposition', content_disposition('bills_info.csv')),
('Content-Type', 'application/vnd.ms-excel')])