Browse Source
[12.0][IMP] excel_import_export, excel_import_export_demo
[12.0][IMP] excel_import_export, excel_import_export_demo
Add report action feature and new examples12.0
Kitti U
5 years ago
committed by
OCA-git-bot
26 changed files with 450 additions and 29 deletions
-
1excel_import_export/__init__.py
-
9excel_import_export/__manifest__.py
-
4excel_import_export/controllers/__init__.py
-
53excel_import_export/controllers/main.py
-
1excel_import_export/models/__init__.py
-
42excel_import_export/models/common.py
-
39excel_import_export/models/ir_report.py
-
5excel_import_export/readme/HISTORY.rst
-
20excel_import_export/readme/USAGE.rst
-
88excel_import_export/static/src/js/report/action_manager_report.js
-
11excel_import_export/views/webclient_templates.xml
-
1excel_import_export_demo/__init__.py
-
8excel_import_export_demo/__manifest__.py
-
2excel_import_export_demo/readme/DESCRIPTION.rst
-
5excel_import_export_demo/readme/HISTORY.rst
-
14excel_import_export_demo/readme/USAGE.rst
-
4excel_import_export_demo/report_action/__init__.py
-
4excel_import_export_demo/report_action/partner_list/__init__.py
-
BINexcel_import_export_demo/report_action/partner_list/partner_list.xlsx
-
10excel_import_export_demo/report_action/partner_list/report.xml
-
37excel_import_export_demo/report_action/partner_list/report_partner_list.py
-
43excel_import_export_demo/report_action/partner_list/report_partner_list.xml
-
29excel_import_export_demo/report_action/partner_list/templates.xml
-
13excel_import_export_demo/report_action/sale_order/report.xml
-
BINexcel_import_export_demo/report_action/sale_order/sale_order_form.xlsx
-
36excel_import_export_demo/report_action/sale_order/templates.xml
@ -0,0 +1,4 @@ |
|||||
|
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) |
||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) |
||||
|
|
||||
|
from . import main |
@ -0,0 +1,53 @@ |
|||||
|
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) |
||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) |
||||
|
|
||||
|
import json |
||||
|
import base64 |
||||
|
import time |
||||
|
from odoo.addons.web.controllers import main as report |
||||
|
from odoo.http import content_disposition, route, request |
||||
|
from odoo.tools.safe_eval import safe_eval |
||||
|
|
||||
|
|
||||
|
class ReportController(report.ReportController): |
||||
|
|
||||
|
@route() |
||||
|
def report_routes(self, reportname, docids=None, converter=None, **data): |
||||
|
if converter == 'excel': |
||||
|
report = request.env['ir.actions.report']._get_report_from_name( |
||||
|
reportname) |
||||
|
context = dict(request.env.context) |
||||
|
if docids: |
||||
|
docids = [int(i) for i in docids.split(',')] |
||||
|
if data.get('options'): |
||||
|
data.update(json.loads(data.pop('options'))) |
||||
|
if data.get('context'): |
||||
|
# Ignore 'lang' here, because the context in data is the one |
||||
|
# from the webclient *but* if the user explicitely wants to |
||||
|
# change the lang, this mechanism overwrites it. |
||||
|
data['context'] = json.loads(data['context']) |
||||
|
if data['context'].get('lang'): |
||||
|
del data['context']['lang'] |
||||
|
context.update(data['context']) |
||||
|
excel = report.with_context(context).render_excel( |
||||
|
docids, data=data |
||||
|
)[0] |
||||
|
excel = base64.decodestring(excel) |
||||
|
report_name = report.report_file |
||||
|
if report.print_report_name and not len(docids) > 1: |
||||
|
obj = request.env[report.model].browse(docids[0]) |
||||
|
report_name = safe_eval(report.print_report_name, |
||||
|
{'object': obj, 'time': time}) |
||||
|
excelhttpheaders = [ |
||||
|
('Content-Type', 'application/vnd.openxmlformats-' |
||||
|
'officedocument.spreadsheetml.sheet'), |
||||
|
('Content-Length', len(excel)), |
||||
|
( |
||||
|
'Content-Disposition', |
||||
|
content_disposition(report_name + '.xlsx') |
||||
|
) |
||||
|
] |
||||
|
return request.make_response(excel, headers=excelhttpheaders) |
||||
|
return super(ReportController, self).report_routes( |
||||
|
reportname, docids, converter, **data |
||||
|
) |
@ -0,0 +1,39 @@ |
|||||
|
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) |
||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) |
||||
|
|
||||
|
from odoo import api, fields, models, _ |
||||
|
from odoo.exceptions import UserError |
||||
|
|
||||
|
|
||||
|
class ReportAction(models.Model): |
||||
|
_inherit = "ir.actions.report" |
||||
|
|
||||
|
report_type = fields.Selection(selection_add=[("excel", "Excel")]) |
||||
|
|
||||
|
@api.model |
||||
|
def render_excel(self, docids, data): |
||||
|
if len(docids) != 1: |
||||
|
raise UserError( |
||||
|
_('Only one id is allowed for excel_import_export')) |
||||
|
xlsx_template = self.env['xlsx.template'].search( |
||||
|
[('fname', '=', self.report_name), ('res_model', '=', self.model)]) |
||||
|
if not xlsx_template or len(xlsx_template) != 1: |
||||
|
raise UserError( |
||||
|
_("Template %s on model %s is not unique!" % |
||||
|
(self.report_name, self.model))) |
||||
|
Export = self.env['xlsx.export'] |
||||
|
return Export.export_xlsx(xlsx_template, self.model, docids[0]) |
||||
|
|
||||
|
@api.model |
||||
|
def _get_report_from_name(self, report_name): |
||||
|
res = super(ReportAction, self)._get_report_from_name(report_name) |
||||
|
if res: |
||||
|
return res |
||||
|
report_obj = self.env['ir.actions.report'] |
||||
|
qwebtypes = ['excel'] |
||||
|
conditions = [ |
||||
|
('report_type', 'in', qwebtypes), |
||||
|
('report_name', '=', report_name), |
||||
|
] |
||||
|
context = self.env['res.users'].context_get() |
||||
|
return report_obj.with_context(context).search(conditions, limit=1) |
@ -0,0 +1,88 @@ |
|||||
|
// Copyright 2019 Ecosoft Co., Ltd.
|
||||
|
// License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html).
|
||||
|
odoo.define("excel_import_export.report", function (require) { |
||||
|
"use strict"; |
||||
|
|
||||
|
var core = require("web.core"); |
||||
|
var ActionManager = require("web.ActionManager"); |
||||
|
var crash_manager = require("web.crash_manager"); |
||||
|
var framework = require("web.framework"); |
||||
|
var session = require("web.session"); |
||||
|
var _t = core._t; |
||||
|
|
||||
|
ActionManager.include({ |
||||
|
|
||||
|
_downloadReportExcel: function (url, actions) { |
||||
|
framework.blockUI(); |
||||
|
var def = $.Deferred(); |
||||
|
var type = "excel"; |
||||
|
var cloned_action = _.clone(actions); |
||||
|
|
||||
|
if (_.isUndefined(cloned_action.data) || |
||||
|
_.isNull(cloned_action.data) || |
||||
|
(_.isObject(cloned_action.data) && _.isEmpty(cloned_action.data))) |
||||
|
{ |
||||
|
if (cloned_action.context.active_ids) { |
||||
|
url += "/" + cloned_action.context.active_ids.join(','); |
||||
|
} |
||||
|
} else { |
||||
|
url += "?options=" + encodeURIComponent(JSON.stringify(cloned_action.data)); |
||||
|
url += "&context=" + encodeURIComponent(JSON.stringify(cloned_action.context)); |
||||
|
} |
||||
|
|
||||
|
var blocked = !session.get_file({ |
||||
|
url: url, |
||||
|
data: { |
||||
|
data: JSON.stringify([url, type]), |
||||
|
}, |
||||
|
success: def.resolve.bind(def), |
||||
|
error: function () { |
||||
|
crash_manager.rpc_error.apply(crash_manager, arguments); |
||||
|
def.reject(); |
||||
|
}, |
||||
|
complete: framework.unblockUI, |
||||
|
}); |
||||
|
if (blocked) { |
||||
|
// AAB: this check should be done in get_file service directly,
|
||||
|
// should not be the concern of the caller (and that way, get_file
|
||||
|
// could return a deferred)
|
||||
|
var message = _t('A popup window with your report was blocked. You ' + |
||||
|
'may need to change your browser settings to allow ' + |
||||
|
'popup windows for this page.'); |
||||
|
this.do_warn(_t('Warning'), message, true); |
||||
|
} |
||||
|
return def; |
||||
|
}, |
||||
|
|
||||
|
_triggerDownload: function (action, options, type) { |
||||
|
var self = this; |
||||
|
var reportUrls = this._makeReportUrls(action); |
||||
|
if (type === "excel") { |
||||
|
return this._downloadReportExcel(reportUrls[type], action).then(function () { |
||||
|
if (action.close_on_report_download) { |
||||
|
var closeAction = {type: 'ir.actions.act_window_close'}; |
||||
|
return self.doAction(closeAction, _.pick(options, 'on_close')); |
||||
|
} else { |
||||
|
return options.on_close(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
return this._super.apply(this, arguments); |
||||
|
}, |
||||
|
|
||||
|
_makeReportUrls: function (action) { |
||||
|
var reportUrls = this._super.apply(this, arguments); |
||||
|
reportUrls.excel = '/report/excel/' + action.report_name; |
||||
|
return reportUrls; |
||||
|
}, |
||||
|
|
||||
|
_executeReportAction: function (action, options) { |
||||
|
var self = this; |
||||
|
if (action.report_type === 'excel') { |
||||
|
return self._triggerDownload(action, options, 'excel'); |
||||
|
} |
||||
|
return this._super.apply(this, arguments); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
}); |
@ -0,0 +1,11 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<!-- |
||||
|
Copyright 2019 Ecosoft Co., Ltd. |
||||
|
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).--> |
||||
|
<odoo> |
||||
|
<template id="assets_backend" inherit_id="web.assets_backend"> |
||||
|
<xpath expr="." position="inside"> |
||||
|
<script type="text/javascript" src="/excel_import_export/static/src/js/report/action_manager_report.js"/> |
||||
|
</xpath> |
||||
|
</template> |
||||
|
</odoo> |
@ -1,3 +1,8 @@ |
|||||
|
12.0.1.0.0 (2019-08-09) |
||||
|
~~~~~~~~~~~~~~~~~~~~~~~ |
||||
|
|
||||
|
* Add 2 new examples using report action, 1) sale_order 2) partner_list |
||||
|
|
||||
12.0.1.0.0 (2019-02-24) |
12.0.1.0.0 (2019-02-24) |
||||
~~~~~~~~~~~~~~~~~~~~~~~ |
~~~~~~~~~~~~~~~~~~~~~~~ |
||||
|
|
||||
|
@ -1,11 +1,19 @@ |
|||||
**Use Case 1:** Export/Import Excel on existing document |
|
||||
|
**Example 1:** Export/Import Excel on existing document |
||||
|
|
||||
To test this use case, go to any Sales Order and use Export Excel or Import Excel in action menu. |
To test this use case, go to any Sales Order and use Export Excel or Import Excel in action menu. |
||||
|
|
||||
**Use Case 2:** Import Excel Files |
|
||||
|
**Example 2:** Import Excel Files |
||||
|
|
||||
To test this use case, go to Settings > Excel Import/Export > Sample Import Sales Order |
To test this use case, go to Settings > Excel Import/Export > Sample Import Sales Order |
||||
|
|
||||
**Use Case 3:** Create Excel Report |
|
||||
|
**Example 3:** Create Excel Report |
||||
|
|
||||
To test this use case, go to Settings > Excel Import/Export > Sample Sales Report |
To test this use case, go to Settings > Excel Import/Export > Sample Sales Report |
||||
|
|
||||
|
**Example 4:** Printout Excel on existing document, using report action |
||||
|
|
||||
|
To test this use case, go to any Sales Order and click print "Quotation / Order (.xlsx)". |
||||
|
|
||||
|
**Example 5:** Run Partner List Report, using report action |
||||
|
|
||||
|
To test this use case, go to menu Sales > Reporting > Partner List Report |
@ -0,0 +1,4 @@ |
|||||
|
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) |
||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) |
||||
|
|
||||
|
from . import partner_list |
@ -0,0 +1,4 @@ |
|||||
|
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) |
||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) |
||||
|
|
||||
|
from . import report_partner_list |
@ -0,0 +1,10 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<report id='action_report_partner_excel' |
||||
|
string='Partner List (.xlsx)' |
||||
|
model='report.partner.list' |
||||
|
name='partner_list.xlsx' |
||||
|
file='partner_list' |
||||
|
report_type='excel' |
||||
|
/> |
||||
|
</odoo> |
@ -0,0 +1,37 @@ |
|||||
|
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) |
||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) |
||||
|
|
||||
|
from odoo import models, fields, api |
||||
|
|
||||
|
|
||||
|
class ReportPartnerList(models.TransientModel): |
||||
|
_name = 'report.partner.list' |
||||
|
_description = 'Wizard for report.partner.list' |
||||
|
|
||||
|
partner_ids = fields.Many2many( |
||||
|
comodel_name='res.partner', |
||||
|
) |
||||
|
supplier = fields.Boolean( |
||||
|
default=True, |
||||
|
) |
||||
|
customer = fields.Boolean( |
||||
|
default=True, |
||||
|
) |
||||
|
results = fields.Many2many( |
||||
|
'res.partner', |
||||
|
string='Results', |
||||
|
compute='_compute_results', |
||||
|
help='Use compute fields, so there is nothing store in database', |
||||
|
) |
||||
|
|
||||
|
@api.multi |
||||
|
def _compute_results(self): |
||||
|
""" On the wizard, result will be computed and added to results line |
||||
|
before export to excel by report_excel action |
||||
|
""" |
||||
|
self.ensure_one() |
||||
|
domain = ['|', ('supplier', '=', self.supplier), |
||||
|
('customer', '=', self.customer)] |
||||
|
if self.partner_ids: |
||||
|
domain.append(('id', 'in', self.partner_ids.ids)) |
||||
|
self.results = self.env['res.partner'].search(domain, order='id') |
@ -0,0 +1,43 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
|
||||
|
<record id="partner_list_wizard" model="ir.ui.view"> |
||||
|
<field name="name">partner.list.wizard</field> |
||||
|
<field name="model">report.partner.list</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form> |
||||
|
<group> |
||||
|
<group> |
||||
|
<field name="partner_ids" widget="many2many_tags"/> |
||||
|
</group> |
||||
|
<group> |
||||
|
<field name="customer"/> |
||||
|
<field name="supplier"/> |
||||
|
</group> |
||||
|
</group> |
||||
|
<footer> |
||||
|
<button name='%(excel_import_export_demo.action_report_partner_excel)d' |
||||
|
type='action' string='Execute' |
||||
|
class='oe_highlight'/> |
||||
|
<button special='cancel' |
||||
|
string='Cancel'/> |
||||
|
</footer> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id='action_report_partner_list' model='ir.actions.act_window'> |
||||
|
<field name='name'>Partner List Report</field> |
||||
|
<field name='res_model'>report.partner.list</field> |
||||
|
<field name='view_type'>form</field> |
||||
|
<field name='view_mode'>form</field> |
||||
|
<field name='target'>new</field> |
||||
|
</record> |
||||
|
|
||||
|
<menuitem id="menu_report_partner_list" |
||||
|
parent="sale.menu_sale_report" |
||||
|
action="action_report_partner_list" |
||||
|
name="Partner List Report" |
||||
|
/> |
||||
|
|
||||
|
</odoo> |
@ -0,0 +1,29 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
|
||||
|
<record id="partner_list_xlsx_template" model="xlsx.template"> |
||||
|
<field name="res_model">report.partner.list</field> |
||||
|
<field name="fname">partner_list.xlsx</field> |
||||
|
<field name="name">Partner List Report Template</field> |
||||
|
<field name="description">Sample Partner List Report Template for testing</field> |
||||
|
<field name="input_instruction"> |
||||
|
{ |
||||
|
'__EXPORT__': { |
||||
|
1: { |
||||
|
'results': { |
||||
|
'A4': 'id', |
||||
|
'B4': 'name${value or ""}#{style=text}', |
||||
|
'C4': 'phone${value or ""}#{style=text}', |
||||
|
'D4': 'email${value or ""}#{style=text}', |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
} |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<function model="xlsx.template" name="load_xlsx_template"> |
||||
|
<value eval="[ref('partner_list_xlsx_template')]"/> |
||||
|
</function> |
||||
|
|
||||
|
</odoo> |
@ -0,0 +1,13 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
|
||||
|
<report id='action_report_saleorder_excel' |
||||
|
string='Quotation / Order (.xlsx)' |
||||
|
model='sale.order' |
||||
|
name='sale_order_form.xlsx' |
||||
|
file='sale_order' |
||||
|
print_report_name="(object.state in ('draft', 'sent') and 'Quotation - %s' % (object.name)) or 'Order - %s' % (object.name)" |
||||
|
report_type='excel' |
||||
|
/> |
||||
|
|
||||
|
</odoo> |
@ -0,0 +1,36 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
|
||||
|
<record id="sale_order_excel_template" model="xlsx.template"> |
||||
|
<field name="res_model">sale.order</field> |
||||
|
<field name="fname">sale_order_form.xlsx</field> |
||||
|
<field name="name">Sale Order Template</field> |
||||
|
<field name="description">Sample Sales Order Template for testing</field> |
||||
|
<field name="input_instruction"> |
||||
|
{ |
||||
|
'__EXPORT__': { |
||||
|
'sale_order': { |
||||
|
'_HEAD_': { |
||||
|
'B2': 'partner_id.display_name${value or ""}#{align=left;style=text}', |
||||
|
'B3': 'name${value or ""}#{align=left;style=text}', |
||||
|
}, |
||||
|
'order_line': { |
||||
|
'A6': 'product_id.display_name${value or ""}#{style=text}', |
||||
|
'B6': 'name${value or ""}#{style=text}', |
||||
|
'C6': 'product_uom_qty${value or 0}#{style=number}', |
||||
|
'D6': 'product_uom.name${value or ""}#{style=text}', |
||||
|
'E6': 'price_unit${value or 0}#{style=number}', |
||||
|
'F6': 'tax_id${value and ",".join([x.display_name for x in value]) or ""}', |
||||
|
'G6': 'price_subtotal${value or 0}#{style=number}@{sum}', |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
} |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<function model="xlsx.template" name="load_xlsx_template"> |
||||
|
<value eval="[ref('sale_order_excel_template')]"/> |
||||
|
</function> |
||||
|
|
||||
|
</odoo> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue