odoo excel报表开发总结

  1. 首先定义菜单然后是菜单动作 然后初始化一个报表动作
     <!--        菜单视图  出库凭证单据报表-->
    <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
  1. 还有两个关键方法 查看方法 和 下载方法,第一个是查看第二个是下载
@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"/>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容