Browse Source

[ADD] report_label

12.0
Ivàn Todorovich 3 years ago
parent
commit
c1ab63d868
  1. 2
      report_label/__init__.py
  2. 26
      report_label/__manifest__.py
  3. 27
      report_label/data/paperformat_label.xml
  4. 33
      report_label/demo/demo.xml
  5. 3
      report_label/models/__init__.py
  6. 14
      report_label/models/ir_actions_report.py
  7. 57
      report_label/models/ir_actions_server.py
  8. 38
      report_label/models/report_paperformat_label.py
  9. 10
      report_label/readme/CONFIGURE.rst
  10. 6
      report_label/readme/CONTRIBUTORS.rst
  11. 1
      report_label/readme/DESCRIPTION.rst
  12. 3
      report_label/readme/ROADMAP.rst
  13. 5
      report_label/readme/USAGE.rst
  14. 49
      report_label/reports/report_label.xml
  15. 3
      report_label/security/ir.model.access.csv
  16. BIN
      report_label/static/description/configure_paperformat.png
  17. BIN
      report_label/static/description/configure_report_label.png
  18. BIN
      report_label/static/description/label_wizard.png
  19. 1
      report_label/tests/__init__.py
  20. 21
      report_label/tests/test_report_label.py
  21. 40
      report_label/views/ir_actions_server.xml
  22. 70
      report_label/views/report_paperformat_label.xml
  23. 1
      report_label/wizards/__init__.py
  24. 103
      report_label/wizards/report_label_wizard.py
  25. 36
      report_label/wizards/report_label_wizard.xml

2
report_label/__init__.py

@ -0,0 +1,2 @@
from . import models
from . import wizards

26
report_label/__manifest__.py

@ -0,0 +1,26 @@
{
'name': 'Report Labels',
'version': '12.0.1.0.0',
'summary': 'Print configurable self-adhesive labels reports',
'author': 'Iván Todorovich, Moka Tourisme, Odoo Community Association (OCA)',
'website': 'https://github.com/OCA/reporting-engine',
'license': 'AGPL-3',
'category': 'Reporting',
'maintainers': [
'ivantodorovich'
],
'depends': [
'base',
],
'data': [
'security/ir.model.access.csv',
'data/paperformat_label.xml',
'views/ir_actions_server.xml',
'views/report_paperformat_label.xml',
'reports/report_label.xml',
'wizards/report_label_wizard.xml',
],
'demo': [
'demo/demo.xml',
]
}

27
report_label/data/paperformat_label.xml

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="agipa_114016" model="report.paperformat.label">
<field name="name">Label: Agipa 114016</field>
<field name="format">A5</field>
<field name="orientation">Portrait</field>
<field name="margin_top" eval="5"/>
<field name="margin_right" eval="13"/>
<field name="margin_bottom" eval="0"/>
<field name="margin_left" eval="13"/>
<field name="dpi" eval="82"/>
<field name="label_width" eval="38"/>
<field name="label_height" eval="19"/>
<field name="label_padding_top" eval="1"/>
<field name="label_padding_right" eval="1"/>
<field name="label_padding_bottom" eval="1"/>
<field name="label_padding_left" eval="1"/>
<field name="label_margin_top" eval="1"/>
<field name="label_margin_right" eval="1"/>
<field name="label_margin_bottom" eval="1"/>
<field name="label_margin_left" eval="1"/>
</record>
</data>
</odoo>

33
report_label/demo/demo.xml

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="label_template_partner_address" name="Partner Label: Address">
<address
t-field="record.self"
t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}'
/>
</template>
<record id="report_paperformat_label_partner_address" model="report.paperformat.label">
<field name="name">Partner Label</field>
<field name="format">A4</field>
<field name="label_height" eval="42.3"/>
<field name="label_width" eval="60"/>
<field name="label_padding_top" eval="5"/>
<field name="label_padding_right" eval="5"/>
<field name="label_padding_bottom" eval="5"/>
<field name="label_padding_left" eval="5"/>
</record>
<record id="actions_server_label_partner_address" model="ir.actions.server">
<field name="name">Print Address Labels</field>
<field name="state">report_label</field>
<field name="model_id" ref="base.model_res_partner"/>
<field name="label_paperformat_id" ref="report_paperformat_label_partner_address"/>
<field name="label_template">report_label.label_template_partner_address</field>
</record>
<!-- Create context action -->
<function model="ir.actions.server" eval="[ref('actions_server_label_partner_address')]" name="create_action"/>
</odoo>

3
report_label/models/__init__.py

@ -0,0 +1,3 @@
from . import report_paperformat_label
from . import ir_actions_server
from . import ir_actions_report

14
report_label/models/ir_actions_report.py

@ -0,0 +1,14 @@
from odoo import api, models
class IrActionsReport(models.Model):
_inherit = "ir.actions.report"
@api.model
def get_paperformat(self):
# Allow to define paperformat via context
res = super().get_paperformat()
if self.env.context.get("paperformat_id"):
res = self.env["report.paperformat"].browse(
self.env.context.get("paperformat_id"))
return res

57
report_label/models/ir_actions_server.py

@ -0,0 +1,57 @@
from odoo import api, models, fields
class IrActionsServer(models.Model):
_inherit = "ir.actions.server"
state = fields.Selection(
selection_add=[("report_label", "Print self-adhesive labels")]
)
label_template = fields.Char(
"Label QWeb Template",
help="The QWeb template key to render the labels",
states={
"report_label": [("required", True)]
}
)
label_paperformat_id = fields.Many2one(
"report.paperformat.label",
"Label Paper Format",
states={
"report_label": [("required", True)]
}
)
@api.multi
def report_label_associated_view(self):
""" View the associated qweb templates """
self.ensure_one()
action = self.env.ref('base.action_ui_view', raise_if_not_found=False)
if not action or len(self.label_template.split('.')) < 2:
return False
res = action.read()[0]
res['domain'] = [
('type', '=', 'qweb'),
'|',
('name', 'ilike', self.label_template.split('.')[1]),
('key', '=', self.label_template),
]
return res
@api.model
def run_action_report_label_multi(self, action, eval_context=None):
""" Show report label wizard """
context = dict(self.env.context)
context.update({
"label_template": action.label_template,
"label_paperformat_id": action.label_paperformat_id.id,
"res_model_id": action.model_id.id,
})
return {
"name": action.name,
"type": "ir.actions.act_window",
"res_model": "report.label.wizard",
"context": str(context),
"view_mode": "form",
"target": "new",
}

38
report_label/models/report_paperformat_label.py

@ -0,0 +1,38 @@
from odoo import models, fields
class ReportPaperformatLabel(models.Model):
_name = "report.paperformat.label"
_inherits = {"report.paperformat": "paperformat_id"}
_description = "Label Paper Format"
paperformat_id = fields.Many2one(
"report.paperformat",
string="Paper Format",
required=True,
ondelete="cascade",
)
label_width = fields.Float(
"Label Width (mm)",
default=60,
required=True,
)
label_height = fields.Float(
"Label Height (mm)",
default=42.3,
required=True,
)
label_padding_top = fields.Float("Label Padding Top (mm)", default=2)
label_padding_right = fields.Float("Label Padding Right (mm)", default=2)
label_padding_bottom = fields.Float("Label Padding Bottom (mm)", default=2)
label_padding_left = fields.Float("Label Padding Left (mm)", default=2)
label_margin_top = fields.Float("Label Margin Top (mm)", default=2)
label_margin_right = fields.Float("Label Margin Right (mm)", default=2)
label_margin_bottom = fields.Float("Label Margin Bottom (mm)", default=2)
label_margin_left = fields.Float("Label Margin Left (mm)", default=2)
# Overload inherits defaults
orientation = fields.Selection(inherited=True, default="Portrait")
header_spacing = fields.Integer(inherited=True, default=0)
margin_top = fields.Float(inherited=True, default=7)
margin_bottom = fields.Float(inherited=True, default=7)

10
report_label/readme/CONFIGURE.rst

@ -0,0 +1,10 @@
Go to **Settings > Technical > Analysis > Label Paper Format** and create
your self-adhesive label paper formats.
.. image:: ../static/description/configure_paperformat.png
Go to **Settings > Technical > Analysis > Label Report** and create your label
report, and its context action. You'll also need to create or reuse a
QWeb template for you label.
.. image:: ../static/description/configure_report_label.png

6
report_label/readme/CONTRIBUTORS.rst

@ -0,0 +1,6 @@
* Iván Todorovich <ivan.todorovich@gmail.com>
* `Moka Tourisme <https://www.mokatourisme.fr>`_:
* Grégory Schreiner
* Sylvain LE GAL <https://twitter.com/legalsylvain>

1
report_label/readme/DESCRIPTION.rst

@ -0,0 +1 @@
This module allows you to create self-adhesive label printing actions on any model.

3
report_label/readme/ROADMAP.rst

@ -0,0 +1,3 @@
* `wkhtmltopdf` doesn't always respect dpi, and mm measures don't match. For
this matter, it's recommended to use this module along with
`report_wkhtmltopdf_param` and enable `--disable-smart-shrinking`.

5
report_label/readme/USAGE.rst

@ -0,0 +1,5 @@
1. In the target model's tree view, select the records to print.
2. Click *Action* and your label report action name.
3. Select the number of labels per record to print, and click Print.
.. image:: ../static/description/label_wizard.png

49
report_label/reports/report_label.xml

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="report_label_template">
<t t-call="web.basic_layout">
<t t-set="full_width" t-value="True"/>
<t t-set="label_style">
height: <t t-esc="label_format['label_height']"/>mm;
width: <t t-esc="label_format['label_width']"/>mm;
padding-top: <t t-esc="label_format['label_padding_top']"/>mm;
padding-right: <t t-esc="label_format['label_padding_right']"/>mm;
padding-bottom: <t t-esc="label_format['label_padding_bottom']"/>mm;
padding-left: <t t-esc="label_format['label_padding_left']"/>mm;
margin-top: <t t-esc="label_format['label_margin_top']"/>mm;
margin-right: <t t-esc="label_format['label_margin_right']"/>mm;
margin-bottom: <t t-esc="label_format['label_margin_bottom']"/>mm;
margin-left: <t t-esc="label_format['label_margin_left']"/>mm;
display: inline-block;
overflow: hidden;
float: left;
position: relative;
page-break-inside: avoid;
box-sizing: border-box;
</t>
<!-- Offset: Skip the first [offset] labels -->
<t t-foreach="range(0, offset)" t-as="i">
<div t-att-style="label_style"></div>
</t>
<t t-foreach="lines" t-as="line">
<t t-foreach="range(0, line['quantity'])" t-as="i">
<div t-att-style="label_style">
<t t-call="{{label_template}}">
<t t-set="record" t-value="docs.env[res_model].browse(line['res_id'])"/>
</t>
</div>
</t>
</t>
</t>
</template>
<record id="report_label" model="ir.actions.report">
<field name="name">Label Report</field>
<field name="model">report.label.wizard</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">report_label.report_label_template</field>
<field name="report_file">report_label.report_label_template</field>
</record>
</odoo>

3
report_label/security/ir.model.access.csv

@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_report_paperformat_label_all,report.paperformat.label all,model_report_paperformat_label,,1,,,
access_report_label_layout_admin,report.paperformat.label admin,model_report_paperformat_label,base.group_system,1,1,1,1

BIN
report_label/static/description/configure_paperformat.png

After

Width: 1920  |  Height: 715  |  Size: 54 KiB

BIN
report_label/static/description/configure_report_label.png

After

Width: 1920  |  Height: 532  |  Size: 37 KiB

BIN
report_label/static/description/label_wizard.png

After

Width: 984  |  Height: 467  |  Size: 22 KiB

1
report_label/tests/__init__.py

@ -0,0 +1 @@
from . import test_report_label

21
report_label/tests/test_report_label.py

@ -0,0 +1,21 @@
from odoo.tests import common
from ast import literal_eval
class TestReportLabel(common.TransactionCase):
def setUp(self):
super().setUp()
self.partner_label = self.env.ref(
"report_label.actions_server_label_partner_address")
def test_01_print_partner_label(self):
self.partner_label.create_action()
action = self.partner_label.run()
model = action["res_model"]
context = literal_eval(action["context"])
context["active_model"] = "res.partner"
context["active_ids"] = self.env["res.partner"].search([]).ids
wizard = self.env[model].with_context(context).create({})
report_action = wizard.print_report()
self.assertEquals(report_action["type"], "ir.actions.report")

40
report_label/views/ir_actions_server.xml

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_server_action_form" model="ir.ui.view">
<field name="model">ir.actions.server</field>
<field name="inherit_id" ref="base.view_server_action_form"/>
<field name="arch" type="xml">
<header position="inside">
<button
name="report_label_associated_view"
type="object"
string="View QWeb templates"
attrs="{'invisible':['|', ('state', '!=', 'report_label'), ('label_template', '=', False)]}"
help="Display a button in the sidebar of related model to open a wizard"
/>
</header>
<field name="type" position="after">
<field name="label_paperformat_id" attrs="{'invisible': [('state', '!=', 'report_label')]}"/>
<field name="label_template" attrs="{'invisible': [('state', '!=', 'report_label')]}"/>
</field>
</field>
</record>
<record id="report_label_action" model="ir.actions.act_window">
<field name="name">Label Reports</field>
<field name="res_model">ir.actions.server</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="domain">[("state", "=", "report_label")]</field>
<field name="context">{"default_state": "report_label"}</field>
</record>
<menuitem
id="report_label_menu"
name="Label Reports"
action="report_label_action"
parent="base.reporting_menuitem"
sequence="3"/>
</odoo>

70
report_label/views/report_paperformat_label.xml

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="paperformat_label_view_form" model="ir.ui.view">
<field name="model">report.paperformat.label</field>
<field name="inherit_id" ref="base.paperformat_view_form"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<!-- Hide irrelevant fields -->
<field name="header_line" position="attributes">
<attribute name="invisible">1</attribute>
</field>
<field name="header_spacing" position="attributes">
<attribute name="invisible">1</attribute>
</field>
<field name="report_ids" position="attributes">
<attribute name="invisible">1</attribute>
</field>
<!-- Add label paper format fields -->
<form position="inside">
<group name="label">
<group name="label_size" string="Label Size" colspan="2">
<field name="label_height"/>
<field name="label_width"/>
</group>
<group name="label_padding" string="Label Padding">
<field string="Top (mm)" name="label_padding_top"/>
<field string="Right (mm)" name="label_padding_right"/>
<field string="Bottom (mm)" name="label_padding_bottom"/>
<field string="Left (mm)" name="label_padding_left"/>
</group>
<group name="label_margin" string="Label Margin">
<field string="Top (mm)" name="label_margin_top"/>
<field string="Right (mm)" name="label_margin_right"/>
<field string="Bottom (mm)" name="label_margin_bottom"/>
<field string="Left (mm)" name="label_margin_left"/>
</group>
</group>
</form>
</field>
</record>
<record id="paperformat_label_view_tree" model="ir.ui.view">
<field name="model">report.paperformat.label</field>
<field name="arch" type="xml">
<tree string="Label paper format configuration">
<field name="name"/>
<field name="label_height"/>
<field name="label_width"/>
</tree>
</field>
</record>
<record id="paperformat_label_action" model="ir.actions.act_window">
<field name="name">Label paper format configuration</field>
<field name="res_model">report.paperformat.label</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="domain">[]</field>
<field name="context">{}</field>
</record>
<menuitem
id="paperformat_label_menu"
name="Label Paper Formats"
action="paperformat_label_action"
parent="base.reporting_menuitem"
sequence="2"/>
</odoo>

1
report_label/wizards/__init__.py

@ -0,0 +1 @@
from . import report_label_wizard

103
report_label/wizards/report_label_wizard.py

@ -0,0 +1,103 @@
from odoo import api, models, fields
class ReportLabelWizard(models.TransientModel):
_name = "report.label.wizard"
_description = "Report Label Wizard"
@api.model
def _default_line_ids(self):
""" Compute line_ids based on context """
active_model = self.env.context.get("active_model")
active_ids = self.env.context.get("active_ids", [])
if not active_model or not active_ids:
return False
return [
(0, 0, {
"res_id": res_id,
"quantity": 1,
})
for res_id in active_ids
]
model_id = fields.Many2one(
"ir.model",
"Model",
required=True,
default=lambda self: self.env.context.get("res_model_id"),
)
label_paperformat_id = fields.Many2one(
"report.paperformat.label",
"Label Paper Format",
readonly=True,
required=True,
default=lambda self: self.env.context.get("label_paperformat_id"),
)
label_template = fields.Char(
"Label QWeb Template",
readonly=True,
required=True,
default=lambda self: self.env.context.get("label_template"),
)
offset = fields.Integer(
help="Number of labels to skip when printing",
)
line_ids = fields.One2many(
"report.label.wizard.line",
"wizard_id",
"Lines",
default=_default_line_ids,
required=True,
)
def _prepare_report_data(self):
self.ensure_one()
return {
"label_format": self.label_paperformat_id.read()[0],
"label_template": self.label_template,
"offset": self.offset,
"res_model": self.model_id.model,
"lines": [
{
"res_id": line.res_id,
"quantity": line.quantity,
}
for line in self.line_ids
],
}
def print_report(self):
self.ensure_one()
report = self.env.ref("report_label.report_label")
action = report.report_action(self, data=self._prepare_report_data())
action["context"] = {
"paperformat_id": self.label_paperformat_id.paperformat_id.id,
}
return action
class ReportLabelWizardLine(models.TransientModel):
_name = "report.label.wizard.line"
_description = "Report Label Wizard Line"
_order = "sequence"
wizard_id = fields.Many2one(
"report.label.wizard",
"Wizard",
required=True,
ondelete="cascade",
)
sequence = fields.Integer(default=10)
res_id = fields.Integer("Resource ID", required=True)
res_name = fields.Char(compute="_compute_res_name")
quantity = fields.Integer(default=1, required=True)
@api.depends("wizard_id.model_id", "res_id")
def _compute_res_name(self):
wizard = self.mapped("wizard_id")
wizard.ensure_one()
res_model = wizard.model_id.model
res_ids = self.mapped("res_id")
names_map = dict(self.env[res_model].browse(res_ids).name_get())
for rec in self:
rec.res_name = names_map.get(rec.res_id)

36
report_label/wizards/report_label_wizard.xml

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="report_label_wizard_view_form" model="ir.ui.view">
<field name="model">report.label.wizard</field>
<field name="arch" type="xml">
<form string="Print Labels">
<sheet>
<group>
</group>
<field name="line_ids">
<tree editable="bottom" create="false">
<field name="sequence" widget="handle"/>
<field name="res_id" invisible="1"/>
<field name="res_name" string="Record"/>
<field name="quantity"/>
</tree>
</field>
<group name="settings" string="Settings">
<group>
<field name="offset"/>
<field name="model_id" invisible="1"/>
<field name="label_paperformat_id"/>
<field name="label_template" invisible="1"/>
</group>
</group>
</sheet>
<footer>
<button name="print_report" string="Print" type="object" icon="fa-print" class="oe_highlight"/>
<button special="cancel" string="Cancel"/>
</footer>
</form>
</field>
</record>
</odoo>
Loading…
Cancel
Save