- 首先定义菜单然后是菜单动作 然后初始化一个报表动作
<!-- 菜单视图 出库凭证单据报表-->
<record id="action_stock_out_voucher_report" model="ir.actions.act_window">
<field name="name">Stock Out Voucher Report</field>
<field name="res_model">kthrp.chanyi.stock.out.voucher.report</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<!-- 菜单视图 出库凭证单据报表-->
<menuitem name="Stock Out Voucher Report"
action="action_stock_out_voucher_report"
id="chanyi_menu_stock_out_voucher_report"
parent="kthrp_stock.menu_stock_out_in_query_report"
sequence="40"/>
<!-- 报表-->
<record id="kthrp_chanyi_stock_out_voucher_report_xlsx" model="ir.actions.report.xml">
<field name="name">Chanyi Stock Out Voucher Report</field>
<field name="model">kthrp.chanyi.stock.out.voucher.report</field>
<field name="type">ir.actions.report.xml</field>
<field name="report_name">kthrp.chanyi.stock.out.voucher.report.xlsx</field>
<field name="report_type">xlsx</field>
<field name="auto" eval="False"/>
</record>
2.首先定义瞬时模型和需要的字段
class KthrpChanyiStockOutVoucher(models.TransientModel):
_name = "kthrp.chanyi.stock.out.voucher.report"
_description = "chanyi Kthrp Stock Out Voucher Report"
# 客户
customer_id = fields.Many2one(string="Customer", comodel_name="kthrp.base.global.partner", domain="[('customer','=',True)]")
# 公司
company_id = fields.Many2one(string='Company', comodel_name="res.company", default=lambda self: self.env.user.company_id.id)
# 处理日期自
date_from = fields.Datetime('Processing Date from')
# 处理日期至
date_to = fields.Datetime('Processing Date to')
# 输出格式
output_format = fields.Selection([('excel', 'EXCEL')], 'Output Format', default='excel', readonly=True)
# 产品类别
product_category_ids = fields.Many2many(comodel_name='product.category', string='Product Category')
3.定义页面参数获取方法
@api.model
def get_datas(self):
tmp_from = datetime.strptime(self.date_from, "%Y-%m-%d %H:%M:%S")
date_from = (tmp_from + timedelta(hours=8)).strftime("%Y-%m-%d %H:%M:%S")
tmp_to = datetime.strptime(self.date_from, "%Y-%m-%d %H:%M:%S")
date_to = (tmp_to + timedelta(hours=8)).strftime("%Y-%m-%d %H:%M:%S")
form = {
'lang': self.env.context['lang'],
'customer_id': self.customer_id,
'company_id': self.company_id,
'product_category_ids': self.product_category_ids,
'date_from': date_from,
'date_to': date_to,
'output_format': self.output_format
}
datas = {
'form': form,
}
return datas
4.获取到参数之后再通过参数过滤得到需要的数据
@api.model
def get_lines(self, datas):
customer_id = datas['form']['customer_id']
company_id = datas['form']['company_id']
product_category_ids = datas['form']['product_category_ids']
date_from = datas['form']['date_from']
date_to = datas['form']['date_to']
# 以销售出库的产品为维度
# self.env['stock.move'].search([('state', '=', 'done')])
stock_move_obj = self.env['stock.move']
code_list = []
for rec in stock_move_obj.search([]):
code_list.append(rec.supplier_id.company_code)
convert = self.env['kthrp.stock.unit.convert']
# 客户=库存移动对应业务伙伴 库存移动状态完成 事务处理类型=库房出库
domain = [('supplier_id.company_code', '=', customer_id.partner_number), ('state', '=', 'done'), ('transaction_type_id.code', '=', 'Stock Shipment')]
# domain = [('transaction_type_id.code', '=', 'Stock Shipment'), ('state', '=', 'done')]
if company_id:
domain += [('company_id', '=', company_id.id)]
if product_category_ids:
centralized_cls_ids = []
for rec in product_category_ids:
centralized_cls_ids.append(rec.centralized_cls_id.id)
domain += [('centralized_cls_id', 'in', centralized_cls_ids)]
if date_from:
domain += [('date', '>=', date_from)]
if date_to:
domain += [('date', '<=', date_to)]
lines = stock_move_obj.search(domain)
field_dict = {}
for line in lines:
ratio = convert.convert_unit_ratio(line.product_id.id, line.product_id.uom_id.id,
line.actual_unit_id.id)
product_number = line.product_id.product_number
# 如果产品编号不在字典中 添加到字典中
if product_number not in field_dict.keys():
field_dict[product_number] = line.actual_qty * ratio
else:
field_dict[product_number] += line.actual_qty * ratio
datalist = []
field_list = []
# 序号
number = 1
for line in lines:
record = {}
product_number = line.product_id.product_number
# 如果该产品不在产品编码列表中
if product_number not in field_list:
record['origin'] = number
record['product_number'] = line.product_id.product_number
record['product_id'] = line.product_id.name
record['specification'] = line.specification or ''
record['actual_qty'] = round(field_dict[product_number], 2)
record['actual_unit_id'] = line.actual_unit_id.name
datalist.append(record)
number += 1
field_list.append(product_number)
# 分页
datalist = [datalist[i:i + 10] for i in range(0, len(datalist), 10)]
return datalist
- 还有两个关键方法 查看方法 和 下载方法,第一个是查看第二个是下载
@api.multi
def report_view(self):
datas = self.get_datas()
lines = self.get_lines(datas)
# 上下文携带数据
context = {}
context['customer_id'] = self.customer_id.name
context['date_from'] = datas['form']['date_from']
context['date_to'] = datas['form']['date_to']
context['name'] = _('Stock Out Voucher Report')
context['lines'] = lines
return {
'name': _('Stock Out Voucher Report'),
'type': 'ir.actions.client',
'tag': 'stock_out_voucher_report',
'context': context,
}
@api.multi
def report_print(self):
name = _('Stock Out Voucher Report')
report_name = 'kthrp.chanyi.stock.out.voucher.report.xlsx'
return {
'name': name,
'type': 'ir.actions.report.xml',
'report_name': report_name,
}
6.定义解析类和打印数据的类
_ir_translation_name = 'stock.out.voucher.report.xlsx'
class KthrpChanyiStockOutVoucherReportParse(report_sxw.rml_parse):
def __init__(self, cr, uid, name, context):
super(KthrpChanyiStockOutVoucherReportParse, self).__init__(cr, uid, name, context=context)
self.context = context
template_changes = {}
self.localcontext.update({
'datetime': datetime,
'wanted_list': [],
'template_changes': template_changes,
'_': self._,
})
def _(self, src):
lang = self.context.get('lang', 'en_US')
return translate(self.cr, _ir_translation_name, 'report', lang, src) or src
class KthrpChanyiStockOutVoucherReportXLSX(ReportXlsxDevelop):
def generate_xlsx_report(self, wb, data, objs):
# 获取数据
data_para = objs.get_datas()
lines_list = objs.get_lines(data_para)
def get_lang_value(values):
lang = self.env.user.lang
if lang == 'zh_CN':
return values[1]
else:
return values[0]
# 获取样式(详见_get_format方法)
general_formats = self._get_format(wb)
# 列标题 general_formats['rh_header_format']
other_head_left_format = wb.add_format(self.union(self.font_style, {'align': 'left', 'bold': True}))
other_head_right_format = wb.add_format(self.union(self.font_style, {'align': 'right', 'bold': True}))
# 文本格式
font_format_left = {'align': 'left', 'font_size': '10', 'font_name': 'Microsoft YaHei', 'border': 1,
'border_color': 'black', }
font_format_left = wb.add_format(font_format_left)
font_format_right = {'align': 'right', 'font_size': '10', 'font_name': 'Microsoft YaHei', 'border': 1,
'border_color': 'black', }
font_format_right = wb.add_format(font_format_right)
font_format_center = {'align': 'center', 'font_size': '10', 'font_name': 'Microsoft YaHei', 'border': 1,
'border_color': 'black', }
font_format_center = wb.add_format(font_format_center)
# 设置页签
ws = wb.add_worksheet(get_lang_value([u'Stock Out Voucher Report', u'出库凭证']))
blue_fields = [['Origin', u'序号'], ['Product Number', u'产品编码'],
['Product', u'产品'], ['Specification', u'规格'],
['Qty', u'数量'], ['Unit', u'单位']]
# fields = ['origin', 'product_number', 'product_id', 'specification', 'actual_qty', 'actual_unit_id']
# 设置列宽
colums_size = [30, 30, 20, 30, 30, 20]
length = len(colums_size)
for col in range(length):
ws.set_column(col, col, colums_size[col])
row_pos = 0
col_pos = 0
if lines_list:
# 打印数据(数据格式为列表嵌套字典)
for index, lines in enumerate(lines_list):
# 打印标题
row_pos += 1
ws.merge_range(row_pos, col_pos, row_pos, length - 1,
get_lang_value(
[u'Stock Out Voucher Report', u'%s出库凭证' % data_para['form']['company_id'].name]),
general_formats['title_format'])
row_pos += 1
# 打印表头
# merge_range写入,参数(行号, 合并起列号, 行号, 合并至列号, 打印数据, 格式)
# ws.merge_range(row_pos, 0, row_pos, 1, get_lang_value([u'Customer:', u'客户:']) + data_para['form']['customer_id'].name,
# other_head_left_format)
ws.write_rich_string(row_pos, col_pos,
general_formats['font_bold_format'],
get_lang_value([u'Customer:', u'客户:']),
general_formats['font_format'],
data_para['form']['customer_id'].name,
wb.add_format({'align': 'left'}))
value = u'第%d页/共%d页' % (index + 1, len(lines_list))
ws.write(row_pos, 5, get_lang_value([u'Number', value]), general_formats['font_format'])
# ws.merge_range(row_pos, col_pos, general_formats['font_format'])
row_pos += 1
ws.write_rich_string(row_pos, col_pos, general_formats['font_bold_format'],
get_lang_value([u'Date From:', u'处理日期自:']),
general_formats['font_format'], data_para['form']['date_from'],
wb.add_format({'align': 'left'}))
ws.write_rich_string(row_pos, col_pos + length - 1, general_formats['font_bold_format'],
get_lang_value([u'Date To::', u'处理日期至:']),
general_formats['font_right_format'], data_para['form']['date_to'],
wb.add_format({'align': 'right'}))
row_pos += 1
# 设置标题栏名称,宽度
columns = []
for field in blue_fields:
columns.append((field[1], 20))
# 打印标题栏
for i, column in enumerate(columns):
ws.set_column(col_pos + i, col_pos + i, column[1])
ws.write(row_pos, col_pos + i, column[0], general_formats['rh_header_format'])
row_pos += 1
# 固定标题
ws.freeze_panes(row_pos, 0)
for line in lines:
ws.write(row_pos, 0, line['origin'], font_format_center)
ws.write(row_pos, 1, line['product_number'], font_format_left)
ws.write(row_pos, 2, line['product_id'], font_format_left)
ws.write(row_pos, 3, line['specification'], font_format_left)
ws.write(row_pos, 4, line['actual_qty'], font_format_right)
ws.write(row_pos, 5, line['actual_unit_id'], font_format_left)
row_pos += 1
# 打印报表信息(表尾)(固定打印:打印人,打印时间)
ws.write_rich_string(row_pos, col_pos, general_formats['font_bold_format'], _('Printer:'),
general_formats['font_format'], objs.env.user.related_employee_id.name_get()[0][1])
ws.write_rich_string(row_pos, col_pos + length - 1, general_formats['font_bold_right_format'], _('Print Time:')
, general_formats['font_right_format'], get_context_datetime(objs, fields.Datetime.now()),
wb.add_format({'align': 'right'}))
# 首行留空
row_pos += 1
else:
# 打印标题
row_pos += 1
ws.merge_range(row_pos, col_pos, row_pos, length - 1,
get_lang_value(
[u'Stock Out Voucher Report', u'%s出库凭证' % data_para['form']['company_id'].name]),
general_formats['title_format'])
row_pos += 1
# 打印表头
# merge_range写入,参数(行号, 合并起列号, 行号, 合并至列号, 打印数据, 格式)
# ws.merge_range(row_pos, 0, row_pos, 1, get_lang_value([u'Customer:', u'客户:']) + data_para['form']['customer_id'].name,
# other_head_left_format)
ws.write_rich_string(row_pos, col_pos,
general_formats['font_bold_format'],
get_lang_value([u'Customer:', u'客户:']),
general_formats['font_format'],
data_para['form']['customer_id'].name,
wb.add_format({'align': 'left'}))
ws.write(row_pos, 5, get_lang_value([u'Number', u'第1页/共1页']), general_formats['font_format'])
# ws.merge_range(row_pos, col_pos, general_formats['font_format'])
row_pos += 1
ws.write_rich_string(row_pos, col_pos, general_formats['font_bold_format'],
get_lang_value([u'Date From:', u'处理日期自:']),
general_formats['font_format'], data_para['form']['date_from'],
wb.add_format({'align': 'left'}))
ws.write_rich_string(row_pos, col_pos + length - 1, general_formats['font_bold_format'],
get_lang_value([u'Date To::', u'处理日期至:']),
general_formats['font_right_format'], data_para['form']['date_to'],
wb.add_format({'align': 'right'}))
row_pos += 1
# 设置标题栏名称,宽度
columns = []
for field in blue_fields:
columns.append((field[1], 20))
# 打印标题栏
for i, column in enumerate(columns):
ws.set_column(col_pos + i, col_pos + i, column[1])
ws.write(row_pos, col_pos + i, column[0], general_formats['rh_header_format'])
row_pos += 1
# 固定标题
ws.freeze_panes(row_pos, 0)
# 打印报表信息(表尾)(固定打印:打印人,打印时间)
ws.write_rich_string(row_pos, col_pos, general_formats['font_bold_format'], _('Printer:'),
general_formats['font_format'], objs.env.user.related_employee_id.name_get()[0][1])
ws.write_rich_string(row_pos, col_pos + length - 1, general_formats['font_bold_right_format'],
_('Print Time:')
, general_formats['font_right_format'],
get_context_datetime(objs, fields.Datetime.now()),
wb.add_format({'align': 'right'}))
KthrpChanyiStockOutVoucherReportXLSX('report.kthrp.chanyi.stock.out.voucher.report.xlsx', 'kthrp.chanyi.stock.out.voucher.report',
parser=KthrpChanyiStockOutVoucherReportParse)
解析类是固定格式,只用修改类名即可,固定格式
7.上面是下载逻辑,接下来所示查看的逻辑,在上述定义了查看方法之后,对应的会有一个js动作
odoo.define('kthrp_chanyi_ext.stock_out_voucher_report', function (require) {
"use strict";
var core = require('web.core');
var Widget = require('web.Widget');
var Model = require('web.Model');
var data = require('web.data');
var QWeb = core.qweb;
var StockOutVoucherReport = Widget.extend({
init: function (parent, context) {
this._super(parent);
if (context.context.active_id) {
this.report_id = context.context.active_id;
sessionStorage.setItem("report_id", this.report_id);
}
else {
this.report_id = parseInt(sessionStorage.getItem("report_id"));
}
this.context = context.context;
},
start:function () {
var self = this;
var context = self.context;
this.$el.append(QWeb.render("StockOutVoucherReport",{
widget: this,
name: context.name,
customer_id: context.customer_id,
date_from: context.date_from,
date_to: context.date_to,
lines: context.lines,
}
));
this.$("button").click(function () {
var model_name = "kthrp.chanyi.stock.out.voucher.report";
var report_model = new Model(model_name);
report_model.call("report_print",[self.report_id])
.then(function (action) {
action.context = {
'active_id': self.report_id,
'active_model': 'kthrp.chanyi.stock.out.voucher.report',
'active_ids': [self.report_id]
};
self.do_action(action);
});
});
},
});
core.action_registry.add('stock_out_voucher_report', StockOutVoucherReport);
})
通过js动作打开对应的模板文件,然后渲染出来
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="StockOutVoucherReport">
<div style="margin-left:8px;font-size:20px;border-bottom: 1px solid #cacaca;color:gray;margin-top:2.2px;height:35px;margin-right:8px">
<span t-esc="name"/>
<button type="button" style="float:right;display: inline-block;margin-right:5%;margin-bottom:2.2px;" class="btn btn-primary btn-sm payable-supplier-aging-download">Download</button>
</div>
<t>
<div style="line-height:40px;">
<span style="margin-left: 20px;">Customer:</span>
<span style="margin-left:8px;display: inline-block;"><t t-esc="customer_id"/></span>
</div>
<div style="line-height:40px;">
<span style="margin-left: 20px;">Date From:</span>
<span style="margin-left:8px;display: inline-block;"><t t-esc="date_from"/></span>
<span style="float:right;margin-right: 20px;">
<span>Date To:</span>
<span style="margin-left:8px;display: inline-block;"><t t-esc="date_to"/></span>
</span>
</div>
<div style="height: 100%;position: absolute;width:100%;padding-bottom:80px">
<div class="account_report_table_wrapper" style="overflow:auto;height: 100%;position:relative;margin-left:5px">
<table class="o_list_view table table-condensed table-striped table-expandable-wang" style="margin-left: 1px;width: 99.9%;">
<thead style="background:#D9D9D9;width:100%">
<tr>
<th>Origin</th>
<th>Product Number</th>
<th>Product</th>
<th>Specification</th>
<th>Qty</th>
<th>Unit</th>
</tr>
</thead>
<tbody>
<t t-if="lines">
<t t-foreach="lines" t-as="line">
<t t-foreach="line" t-as="rec">
<tr>
<td align="center" style="border: 1px solid;"><span t-esc="rec['origin']"/></td>
<td align="left" style="border: 1px solid;"><span t-esc="rec['product_number']"/></td>
<td align="left" style="border: 1px solid;"><span t-esc="rec['product_id']"/></td>
<td align="left" style="border: 1px solid;"><span t-esc="rec['specification']"/></td>
<td align="right" style="border: 1px solid;"><span t-esc="rec['actual_qty']"/></td>
<td align="left" style="border: 1px solid;"><span t-esc="rec['actual_unit_id']"/></td>
</tr>
</t>
</t>
</t>
</tbody>
</table>
</div>
</div>
</t>
</t>
</templates>
记住,这两个文件都是定义在static文件目录下的
js文件需要引用声明, 类似于以下方式
<script type="text/javascript"
src="/kthrp_base/static/lib/Resources/dynamsoft.webtwain.config.js"/>