Browse Source

[12.0][IMP] excel_import_export, excel_import_export_demo

Add report action feature and new examples
12.0
Kitti U 5 years ago
committed by OCA-git-bot
parent
commit
a66a12c957
  1. 1
      excel_import_export/__init__.py
  2. 9
      excel_import_export/__manifest__.py
  3. 4
      excel_import_export/controllers/__init__.py
  4. 53
      excel_import_export/controllers/main.py
  5. 1
      excel_import_export/models/__init__.py
  6. 42
      excel_import_export/models/common.py
  7. 39
      excel_import_export/models/ir_report.py
  8. 5
      excel_import_export/readme/HISTORY.rst
  9. 20
      excel_import_export/readme/USAGE.rst
  10. 88
      excel_import_export/static/src/js/report/action_manager_report.js
  11. 11
      excel_import_export/views/webclient_templates.xml
  12. 1
      excel_import_export_demo/__init__.py
  13. 8
      excel_import_export_demo/__manifest__.py
  14. 2
      excel_import_export_demo/readme/DESCRIPTION.rst
  15. 5
      excel_import_export_demo/readme/HISTORY.rst
  16. 14
      excel_import_export_demo/readme/USAGE.rst
  17. 4
      excel_import_export_demo/report_action/__init__.py
  18. 4
      excel_import_export_demo/report_action/partner_list/__init__.py
  19. BIN
      excel_import_export_demo/report_action/partner_list/partner_list.xlsx
  20. 10
      excel_import_export_demo/report_action/partner_list/report.xml
  21. 37
      excel_import_export_demo/report_action/partner_list/report_partner_list.py
  22. 43
      excel_import_export_demo/report_action/partner_list/report_partner_list.xml
  23. 29
      excel_import_export_demo/report_action/partner_list/templates.xml
  24. 13
      excel_import_export_demo/report_action/sale_order/report.xml
  25. BIN
      excel_import_export_demo/report_action/sale_order/sale_order_form.xlsx
  26. 36
      excel_import_export_demo/report_action/sale_order/templates.xml

1
excel_import_export/__init__.py

@ -3,3 +3,4 @@
from . import wizard from . import wizard
from . import models from . import models
from . import controllers

9
excel_import_export/__manifest__.py

@ -2,9 +2,9 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
{ {
'name': 'Excel Import/Export',
'summary': 'Base module for easy way to develop Excel import/export',
'version': '12.0.1.0.1',
'name': 'Excel Import/Export/Report',
'summary': 'Base module for developing Excel import/export/report',
'version': '12.0.1.0.2',
'author': 'Ecosoft,Odoo Community Association (OCA)', 'author': 'Ecosoft,Odoo Community Association (OCA)',
'license': 'AGPL-3', 'license': 'AGPL-3',
'website': 'https://github.com/OCA/server-tools/', 'website': 'https://github.com/OCA/server-tools/',
@ -22,8 +22,9 @@
'wizard/import_xlsx_wizard.xml', 'wizard/import_xlsx_wizard.xml',
'views/xlsx_template_view.xml', 'views/xlsx_template_view.xml',
'views/xlsx_report.xml', 'views/xlsx_report.xml',
'views/webclient_templates.xml',
], ],
'installable': True, 'installable': True,
'development_status': 'alpha',
'development_status': 'beta',
'maintainers': ['kittiu'], 'maintainers': ['kittiu'],
} }

4
excel_import_export/controllers/__init__.py

@ -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

53
excel_import_export/controllers/main.py

@ -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
)

1
excel_import_export/models/__init__.py

@ -6,3 +6,4 @@ from . import xlsx_export
from . import xlsx_import from . import xlsx_import
from . import xlsx_template from . import xlsx_template
from . import xlsx_report from . import xlsx_report
from . import ir_report

42
excel_import_export/models/common.py

@ -180,28 +180,28 @@ def xlrd_get_sheet_by_name(book, name):
raise ValidationError(_("'%s' sheet not found") % (name,)) raise ValidationError(_("'%s' sheet not found") % (name,))
def isfloat(input):
def isfloat(input_val):
try: try:
float(input)
float(input_val)
return True return True
except ValueError: except ValueError:
return False return False
def isinteger(input):
def isinteger(input_val):
try: try:
int(input)
int(input_val)
return True return True
except ValueError: except ValueError:
return False return False
def isdatetime(input):
def isdatetime(input_val):
try: try:
if len(input) == 10:
dt.strptime(input, '%Y-%m-%d')
elif len(input) == 19:
dt.strptime(input, '%Y-%m-%d %H:%M:%S')
if len(input_val) == 10:
dt.strptime(input_val, '%Y-%m-%d')
elif len(input_val) == 19:
dt.strptime(input_val, '%Y-%m-%d %H:%M:%S')
else: else:
return False return False
return True return True
@ -209,18 +209,18 @@ def isdatetime(input):
return False return False
def str_to_number(input):
if isinstance(input, str):
if ' ' not in input:
if isdatetime(input):
return parse(input)
elif isinteger(input):
if not (len(input) > 1 and input[:1] == '0'):
return int(input)
elif isfloat(input):
if not (input.find(".") > 2 and input[:1] == '0'): # 00.123
return float(input)
return input
def str_to_number(input_val):
if isinstance(input_val, str):
if ' ' not in input_val:
if isdatetime(input_val):
return parse(input_val)
elif isinteger(input_val):
if not (len(input_val) > 1 and input_val[:1] == '0'):
return int(input_val)
elif isfloat(input_val):
if not (input_val.find(".") > 2 and input_val[:1] == '0'):
return float(input_val)
return input_val
def csv_from_excel(excel_content, delimiter, quote): def csv_from_excel(excel_content, delimiter, quote):

39
excel_import_export/models/ir_report.py

@ -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)

5
excel_import_export/readme/HISTORY.rst

@ -1,3 +1,8 @@
12.0.1.0.3 (2019-08-09)
~~~~~~~~~~~~~~~~~~~~~~~
* Add report action for report_type = 'excel'
12.0.1.0.2 (2019-08-07) 12.0.1.0.2 (2019-08-07)
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~

20
excel_import_export/readme/USAGE.rst

@ -39,3 +39,23 @@ This create report menu with criteria wizard. (example - excel_import_export_dem
3. Create report model as models.Transient, then define search criteria fields, and get reporing data into ``results`` field -- <report>.py 3. Create report model as models.Transient, then define search criteria fields, and get reporing data into ``results`` field -- <report>.py
4. Create/Design Excel Template File (.xlsx), in the template, name the underlining tab used for report results -- <report_file>.xlsx 4. Create/Design Excel Template File (.xlsx), in the template, name the underlining tab used for report results -- <report_file>.xlsx
5. Create instruction dictionary for report in xlsx.template model -- templates.xml 5. Create instruction dictionary for report in xlsx.template model -- templates.xml
**Note:**
Another option for reporting is to use report action (report_type='excel'), I.e.,
.. code-block:: xml
<report id='action_report_saleorder_excel'
string='Quotation / Order (.xlsx)'
model='sale.order'
name='sale_order.xlsx'
file='sale_order'
report_type='excel'
/>
By using report action, Odoo will find template using combination of model and name, then do the export for the underlining record.
Please see example in excel_import_export_demo/report_action, which shows,
1. Print excel from an active sale.order
2. Run partner list report based on search criteria.

88
excel_import_export/static/src/js/report/action_manager_report.js

@ -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);
}
});
});

11
excel_import_export/views/webclient_templates.xml

@ -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
excel_import_export_demo/__init__.py

@ -3,3 +3,4 @@
from . import import_export_sale_order from . import import_export_sale_order
from . import report_sale_order from . import report_sale_order
from . import report_action

8
excel_import_export_demo/__manifest__.py

@ -1,7 +1,7 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) # Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
{'name': 'Excel Import/Export Demo',
{'name': 'Excel Import/Export/Report Demo',
'version': '12.0.1.0.0', 'version': '12.0.1.0.0',
'author': 'Ecosoft,Odoo Community Association (OCA)', 'author': 'Ecosoft,Odoo Community Association (OCA)',
'license': 'AGPL-3', 'license': 'AGPL-3',
@ -15,6 +15,12 @@
'report_sale_order/templates.xml', 'report_sale_order/templates.xml',
'import_sale_orders/menu_action.xml', 'import_sale_orders/menu_action.xml',
'import_sale_orders/templates.xml', 'import_sale_orders/templates.xml',
# Use report action
'report_action/sale_order/report.xml',
'report_action/sale_order/templates.xml',
'report_action/partner_list/report.xml',
'report_action/partner_list/templates.xml',
'report_action/partner_list/report_partner_list.xml',
], ],
'installable': True, 'installable': True,
'development_status': 'alpha', 'development_status': 'alpha',

2
excel_import_export_demo/readme/DESCRIPTION.rst

@ -3,3 +3,5 @@ This module provide some example use case for excel_import_export
1. Import/Export Sales Order (import_export_sale_order) 1. Import/Export Sales Order (import_export_sale_order)
2. Import New Sales Orders (import_sale_orders) 2. Import New Sales Orders (import_sale_orders)
3. Sales Orders Report (report_sale_order) 3. Sales Orders Report (report_sale_order)
4. Print Quoation / Order (.xlsx) (report_action/sale_order)
5. Run Partner List Report (report_action/partner_list)

5
excel_import_export_demo/readme/HISTORY.rst

@ -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)
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~

14
excel_import_export_demo/readme/USAGE.rst

@ -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

4
excel_import_export_demo/report_action/__init__.py

@ -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

4
excel_import_export_demo/report_action/partner_list/__init__.py

@ -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

BIN
excel_import_export_demo/report_action/partner_list/partner_list.xlsx

10
excel_import_export_demo/report_action/partner_list/report.xml

@ -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>

37
excel_import_export_demo/report_action/partner_list/report_partner_list.py

@ -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')

43
excel_import_export_demo/report_action/partner_list/report_partner_list.xml

@ -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>

29
excel_import_export_demo/report_action/partner_list/templates.xml

@ -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>

13
excel_import_export_demo/report_action/sale_order/report.xml

@ -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>

BIN
excel_import_export_demo/report_action/sale_order/sale_order_form.xlsx

36
excel_import_export_demo/report_action/sale_order/templates.xml

@ -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>
Loading…
Cancel
Save