diff options
| author | Simon Lejeune <sle@openerp.com> | 2014-04-04 13:23:08 +0200 |
|---|---|---|
| committer | Simon Lejeune <sle@openerp.com> | 2014-04-04 13:23:08 +0200 |
| commit | dc8246eb03b37d2be7bff497f656cd183ba8a5c9 (patch) | |
| tree | 863060310ec765e5d7a1204ab7eea260609eee15 /addons | |
| parent | 0cbd5fb87b39683689e9694ba369e5a94ef33e1d (diff) | |
[ADD] sale_layout module allowing to set categories and subtotals on invoice and sale order
bzr revid: sle@openerp.com-20140404112308-aopihnefnf55b4er
Diffstat (limited to 'addons')
| -rw-r--r-- | addons/account/views/report_invoice.xml | 2 | ||||
| -rw-r--r-- | addons/sale/views/report_saleorder.xml | 2 | ||||
| -rw-r--r-- | addons/sale_layout/__init__.py | 1 | ||||
| -rw-r--r-- | addons/sale_layout/__openerp__.py | 44 | ||||
| -rw-r--r-- | addons/sale_layout/data/sale_layout_category_data.xml | 20 | ||||
| -rw-r--r-- | addons/sale_layout/models/__init__.py | 1 | ||||
| -rw-r--r-- | addons/sale_layout/models/sale_layout.py | 137 | ||||
| -rw-r--r-- | addons/sale_layout/security/ir.model.access.csv | 5 | ||||
| -rw-r--r-- | addons/sale_layout/views/report_invoice_layouted.xml | 65 | ||||
| -rw-r--r-- | addons/sale_layout/views/report_quotation_layouted.xml | 70 | ||||
| -rw-r--r-- | addons/sale_layout/views/sale_layout_category_view.xml | 120 | ||||
| -rw-r--r-- | addons/sale_layout/views/sale_layout_template.xml | 42 |
12 files changed, 507 insertions, 2 deletions
diff --git a/addons/account/views/report_invoice.xml b/addons/account/views/report_invoice.xml index bc0dc5e92a2..437c9726bf3 100644 --- a/addons/account/views/report_invoice.xml +++ b/addons/account/views/report_invoice.xml @@ -54,7 +54,7 @@ <th class="text-right">Amount</th> </tr> </thead> - <tbody> + <tbody class="invoice_tbody"> <tr t-foreach="o.invoice_line" t-as="l"> <td><span t-field="l.name"/></td> <td><span t-field="l.quantity"/></td> diff --git a/addons/sale/views/report_saleorder.xml b/addons/sale/views/report_saleorder.xml index c6f237e3ac3..40c8afa045d 100644 --- a/addons/sale/views/report_saleorder.xml +++ b/addons/sale/views/report_saleorder.xml @@ -64,7 +64,7 @@ <th class="text-right">Price</th> </tr> </thead> - <tbody> + <tbody class="sale_tbody"> <tr t-foreach="o.order_line" t-as="l"> <td> <span t-field="l.name"/> diff --git a/addons/sale_layout/__init__.py b/addons/sale_layout/__init__.py new file mode 100644 index 00000000000..bff786c0885 --- /dev/null +++ b/addons/sale_layout/__init__.py @@ -0,0 +1 @@ +import models diff --git a/addons/sale_layout/__openerp__.py b/addons/sale_layout/__openerp__.py new file mode 100644 index 00000000000..b8fcefc6adc --- /dev/null +++ b/addons/sale_layout/__openerp__.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2014-Today OpenERP SA (<http://www.openerp.com>). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +{ + 'name': 'Sale Layout', + 'version': '1.0', + 'sequence': 14, + 'summary': 'Sale Layout, page-break, subtotals, separators, report', + 'description': """ +Manage your sales reports +========================= +With this module you can personnalize the sale order and invoice report with +separators, page-breaks or subtotals. + """, + 'author': 'OpenERP SA', + 'website': 'http://www.openerp.com', + 'depends': ['sale', 'report'], + 'category': 'Sale', + 'data': ['views/sale_layout_category_view.xml', + 'views/report_invoice_layouted.xml', + 'views/report_quotation_layouted.xml', + 'views/sale_layout_template.xml', + 'security/ir.model.access.csv'], + 'demo': ['data/sale_layout_category_data.xml'], + 'installable': True, +} diff --git a/addons/sale_layout/data/sale_layout_category_data.xml b/addons/sale_layout/data/sale_layout_category_data.xml new file mode 100644 index 00000000000..858654e427f --- /dev/null +++ b/addons/sale_layout/data/sale_layout_category_data.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data noupdate="1"> + <record id="sale_layout_cat_1" model="sale_layout.category"> + <field name="name">Services</field> + <field name="subtotal" eval="True"></field> + <field name="separator" eval="True"></field> + <field name="pagebreak" eval="True"></field> + <field name="sequence">1</field> + </record> + + <record id="sale_layout_cat_2" model="sale_layout.category"> + <field name="name">Material</field> + <field name="subtotal" eval="True"></field> + <field name="separator" eval="True"></field> + <field name="pagebreak" eval="False"></field> + <field name="sequence">10</field> + </record> + </data> +</openerp> diff --git a/addons/sale_layout/models/__init__.py b/addons/sale_layout/models/__init__.py new file mode 100644 index 00000000000..9e9b002e402 --- /dev/null +++ b/addons/sale_layout/models/__init__.py @@ -0,0 +1 @@ +import sale_layout diff --git a/addons/sale_layout/models/sale_layout.py b/addons/sale_layout/models/sale_layout.py new file mode 100644 index 00000000000..d6e7e435b99 --- /dev/null +++ b/addons/sale_layout/models/sale_layout.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2014-Today OpenERP SA (<http://www.openerp.com>). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +from openerp.osv import osv, fields +from itertools import groupby + + +def grouplines(self, ordered_lines, sortkey): + """Return lines from a specified invoice or sale order grouped by category""" + grouped_lines = [] + for key, valuesiter in groupby(ordered_lines, sortkey): + group = {} + group['category'] = key + group['lines'] = list(v for v in valuesiter) + + if 'subtotal' in key and key.subtotal is True: + group['subtotal'] = sum(line.price_subtotal for line in group['lines']) + grouped_lines.append(group) + + return grouped_lines + + +class SaleLayoutCategory(osv.Model): + _name = 'sale_layout.category' + _order = 'sequence' + _columns = { + 'name': fields.char('Name', required=True), + 'sequence': fields.integer('Sequence', required=True), + 'subtotal': fields.boolean('Add subtotal'), + 'separator': fields.boolean('Add separator'), + 'pagebreak': fields.boolean('Add pagebreak') + } + + _defaults = { + 'subtotal': True, + 'separator': True, + 'pagebreak': False, + 'sequence': 10 + } + + # We want to forbid edit of a category if it is already linked to a report. + def _check(self, cr, uid, ids): + for cat in self.browse(cr, uid, ids): + invoice_obj = self.pool.get('account.invoice.line') + sale_obj = self.pool.get('sale.order.line') + ids = invoice_obj.search(cr, uid, [('sale_layout_cat_id', '=', cat.id)]) + ids += sale_obj.search(cr, uid, [('sale_layout_cat_id', '=', cat.id)]) + + if len(ids) > 0: + return False + + return True + + _constraints = [( + _check, + 'This category could not be modified nor deleted because it is still used in an invoice or' + ' a sale report.', ['name'] + )] + + +class AccountInvoice(osv.Model): + _inherit = 'account.invoice' + + def sale_layout_lines(self, cr, uid, ids, context, invoice_id, *args, **kwargs): + """ + Returns invoice lines from a specified invoice ordered by + sale_layout_category sequence. Used in sale_layout module. + + :Parameters: + -'invoice_id' (int): specify the concerned invoice. + """ + ordered_lines = self.browse(cr, uid, invoice_id, context=context).invoice_line + # We chose to group first by category model and, if not present, by invoice name + sortkey = lambda x: x.sale_layout_cat_id if x.sale_layout_cat_id else '' + + return grouplines(self, ordered_lines, sortkey) + + +class AccountInvoiceLine(osv.Model): + _inherit = 'account.invoice.line' + _columns = { + 'sale_layout_cat_id': fields.many2one('sale_layout.category', + 'Layout Category'), + 'categ_sequence': fields.related('sale_layout_cat_id', + 'sequence', type='integer', + string='Layout Sequence', store=True) + # Store is intentionally set in order to keep the "historic" order. + } + _order = 'invoice_id, categ_sequence, sequence, id' + + +class SaleOrder(osv.Model): + _inherit = 'sale.order' + + def sale_layout_lines(self, cr, uid, ids, context, order_id, *args, **kwargs): + """ + Returns order lines from a specified sale ordered by + sale_layout_category sequence. Used in sale_layout module. + + :Parameters: + -'order_id' (int): specify the concerned sale order. + """ + ordered_lines = self.browse(cr, uid, order_id, context=context).order_line + sortkey = lambda x: x.sale_layout_cat_id if x.sale_layout_cat_id else '' + + return grouplines(self, ordered_lines, sortkey) + + +class SaleOrderLine(osv.Model): + _inherit = 'sale.order.line' + _columns = { + 'sale_layout_cat_id': fields.many2one('sale_layout.category', + 'Layout Category'), + 'categ_sequence': fields.related('sale_layout_cat_id', + 'sequence', type='integer', + string='Layout Sequence', store=True) + # Store is intentionally set in order to keep the "historic" order. + } + _order = 'order_id, categ_sequence, sequence, id' diff --git a/addons/sale_layout/security/ir.model.access.csv b/addons/sale_layout/security/ir.model.access.csv new file mode 100644 index 00000000000..119fce977d1 --- /dev/null +++ b/addons/sale_layout/security/ir.model.access.csv @@ -0,0 +1,5 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +report_layout_category_1,report_layout_category_1,model_sale_layout_category,base.group_sale_manager,1,1,1,1 +report_layout_category_2,report_layout_category_2,model_sale_layout_category,account.group_account_manager,1,1,1,1 +report_layout_category_3,report_layout_category_3,model_sale_layout_category,base.group_sale_salesman,1,1,1,O +report_layout_category_4,report_layout_category_4,model_sale_layout_category,base.group_sale_salesman_all_leads,1,1,1,0 diff --git a/addons/sale_layout/views/report_invoice_layouted.xml b/addons/sale_layout/views/report_invoice_layouted.xml new file mode 100644 index 00000000000..4c0b1929484 --- /dev/null +++ b/addons/sale_layout/views/report_invoice_layouted.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> +<data> +<template id="report_invoice_layouted" inherit_id="account.report_invoice_document"> + <xpath expr="//table/tbody[@class='invoice_tbody']" position="replace"> + <t t-foreach="o.sale_layout_lines(invoice_id=o.id)" t-as="p"> + <!-- Name of the category --> + <t t-call="sale_layout.category_template"/> + + <!-- Lines associated --> + <t t-foreach="p['lines']" t-as="l"> + <tr> + <td><span t-field="l.name"/></td> + <td><span t-field="l.quantity"/></td> + <td groups="product.group_uom"><span t-field="l.uos_id"/></td> + <td class="text-right"> + <span t-field="l.price_unit"/> + </td> + <td groups="sale.group_discount_per_so_line"><span t-field="l.discount"/></td> + <td class="text-right"> + <span t-esc="', '.join(map(lambda x: x.name, l.invoice_line_tax_id))"/> + </td> + <td class="text-right"> + <span t-field="l.price_subtotal" + t-field-options='{"widget": "monetary", "display_currency": "o.currency_id"}'/> + </td> + </tr> + </t> + + <!-- Subtotal --> + <t t-call="sale_layout.subtotal_template"/> + + <!-- Separator --> + <t t-call="sale_layout.separator_template"/> + + <!-- Pagebreak --> + <t t-if="'pagebreak' in p['category'] and p['category'].pagebreak is True"> + <t t-if="p_index < p_size - 1"> + <![CDATA[ + </tbody> + </table> + <p style="page-break-before:always;"> </p> + <table class="table table-condensed"> + ]]> + <thead> + <tr> + <th>Description</th> + <th>Quantity</th> + <th groups="product.group_uom">Unit of measure</th> + <th class="text-right">Unit Price</th> + <th class="text-right" groups="sale.group_discount_per_so_line">Discount (%)</th> + <th class="text-right">Taxes</th> + <th class="text-right">Amount</th> + </tr> + </thead> + <![CDATA[ + <tbody> + ]]> + </t> + </t> + </t> + </xpath> +</template> +</data> +</openerp> diff --git a/addons/sale_layout/views/report_quotation_layouted.xml b/addons/sale_layout/views/report_quotation_layouted.xml new file mode 100644 index 00000000000..ad04e271e7d --- /dev/null +++ b/addons/sale_layout/views/report_quotation_layouted.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <template id="report_sale_layouted" inherit_id="sale.report_saleorder"> + <xpath expr="//table/tbody[@class='sale_tbody']" position="replace"> + <t t-foreach="o.sale_layout_lines(order_id = o.id)" t-as="p"> + <!-- Name of the category --> + <t t-call="sale_layout.category_template" /> + + <!-- Lines associated --> + <t t-foreach="p['lines']" t-as="l"> + <tr> + <td> + <span t-field="l.name"/> + </td> + <td> + <span t-esc="', '.join(map(lambda x: x.name, l.tax_id))"/> + </td> + <td class="text-right"> + <span t-field="l.product_uom_qty"/> + <span groups="product.group_uom" t-field="l.product_uom"/> + </td> + <td class="text-right"> + <span t-field="l.price_unit"/> + </td> + <td groups="sale.group_discount_per_so_line"> + <span t-field="l.discount"/> + </td> + <td class="text-right"> + <span t-field="l.price_subtotal" + t-field-options='{"widget": "monetary", "display_currency": "o.pricelist_id.currency_id"}'/> + </td> + </tr> + </t> + + <!-- Subtotal --> + <t t-call="sale_layout.subtotal_template" /> + + <!-- Separator --> + <t t-call="sale_layout.separator_template" /> + + <!-- Pagebreak --> + <t t-if="'pagebreak' in p['category'] and p['category'].pagebreak is True"> + <t t-if="p_index < p_size - 1"> + <![CDATA[ + </tbody> + </table> + <p style="page-break-before:always;"> </p> + <table class="table table-condensed"> + ]]> + <thead> + <tr> + <th>Description</th> + <th>Taxes</th> + <th class="text-right">Quantity</th> + <th class="text-right">Unit Price</th> + <th groups="sale.group_discount_per_so_line">Disc.(%)</th> + <th class="text-right">Price</th> + </tr> + </thead> + <![CDATA[ + <tbody> + ]]> + </t> + </t> + </t> + </xpath> + </template> + </data> +</openerp> diff --git a/addons/sale_layout/views/sale_layout_category_view.xml b/addons/sale_layout/views/sale_layout_category_view.xml new file mode 100644 index 00000000000..08f892c127e --- /dev/null +++ b/addons/sale_layout/views/sale_layout_category_view.xml @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <!-- Sale order form --> + <record model="ir.ui.view" id="view_order_form_inherit_1"> + <field name="name">sale.order.form.inherit_1</field> + <field name="model">sale.order</field> + <field name="inherit_id" ref="sale.view_order_form"/> + <field name="arch" type="xml"> + <data> + <xpath expr="//notebook/page/field[@name='order_line']/tree" position="attributes"> + <attribute name="editable"></attribute> + </xpath> + </data> + </field> + </record> + + <record model="ir.ui.view" id="view_order_form_inherit_2"> + <field name="name">sale.order.line.form.inherit_2</field> + <field name="model">sale.order</field> + <field name="inherit_id" ref="sale.view_order_form"/> + <field name="arch" type="xml"> + <data> + <xpath expr="//field[@name='order_line']/form/group/group/field[@name='product_id']" position="before"> + <field name="sale_layout_cat_id"/> + </xpath> + </data> + </field> + </record> + + <!-- Invoice form --> + <record model="ir.ui.view" id="view_invoice_form_inherit_1"> + <field name="name">account.invoice.form.inherit_1</field> + <field name="model">account.invoice</field> + <field name="inherit_id" ref="account.invoice_form"/> + <field name="arch" type="xml"> + <data> + <xpath expr="//notebook/page/field[@name='invoice_line']/tree" position="attributes"> + <attribute name="editable"></attribute> + </xpath> + </data> + </field> + </record> + + <record model="ir.ui.view" id="view_invoice_line_form_inherit_2"> + <field name="name">account.invoice.line.form.inherit_2</field> + <field name="model">account.invoice.line</field> + <field name="inherit_id" ref="account.view_invoice_line_form"/> + <field name="arch" type="xml"> + <data> + <xpath expr="//group/group/field[@name='product_id']" position="before"> + <field name="sale_layout_cat_id"/> + </xpath> + </data> + </field> + </record> + + <!-- Report condiguration --> + <record id="report_configuration_form_view" model="ir.ui.view"> + <field name="name">report.configuration.form.view</field> + <field name="model">sale_layout.category</field> + <field name="arch" type="xml"> + <form string="Report Configuration" version="7.0"> + <group> + <field name="name"/> + <field name="subtotal" widget="checkbox"/> + <field name="separator" widget="checkbox"/> + <field name="pagebreak" widget="checkbox"/> + <field name="sequence"/> + </group> + </form> + </field> + </record> + + <record id="report_configuration_tree_view" model="ir.ui.view"> + <field name="name">report.configuration.form.view</field> + <field name="model">sale_layout.category</field> + <field name="arch" type="xml"> + <tree string="Report Configuration"> + <field name="name"/> + <field name="subtotal" widget="checkbox"/> + <field name="separator" widget="checkbox"/> + <field name="pagebreak" widget="checkbox"/> + <field name="sequence"/> + </tree> + </field> + </record> + + <record id="report_configuration_search_view" model="ir.ui.view" > + <field name="name">report.configuration.search.view</field> + <field name="model">sale_layout.category</field> + <field name="arch" type="xml"> + <search string="Search Name"> + <filter string="Total" domain="[('subtotal','=','True')]"/> + <filter string="Separator" domain="[('separator','=','True')]"/> + <filter string="Break" domain="[('pagebreak','=','True')]"/> + <group string="Group By Name"> + <filter string="Name" context="{'group_by' : 'name'}"/> + </group> + </search> + </field> + </record> + + <record id='report_configuration_action' model='ir.actions.act_window'> + <field name="name">Report Configuration</field> + <field name="res_model">sale_layout.category</field> + <field name="view_type">form</field> + <field name="view_mode">tree,form</field> + </record> + + <menuitem + action="report_configuration_action" + id="Report_configuration" + parent="base.menu_base_config" + sequence="25" + name="Report Layout Categories" + groups="base.group_sale_manager,base.group_sale_salesman,account.group_account_manager,account.group_account_user" + /> + </data> +</openerp> diff --git a/addons/sale_layout/views/sale_layout_template.xml b/addons/sale_layout/views/sale_layout_template.xml new file mode 100644 index 00000000000..7b39ea4c632 --- /dev/null +++ b/addons/sale_layout/views/sale_layout_template.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <template id="category_template"> + <!-- Category name --> + <t t-if="p['category']"> + <tr> + <td colspan="100" class="active" style="font-weight: bold; padding-left: 10px; border-bottom: 1px solid black;"> + <t t-if="p['category'].name"> + <t t-esc="p['category'].name"></t> + </t> + <t t-if="not p['category'].name"> + Uncategorized + </t> + </td> + </tr> + </t> + </template> + <template id="subtotal_template"> + <!-- Subtotal --> + <t t-if="'subtotal' in p['category'] and p['category'].subtotal is True"> + <tr class="text-right"> + <td colspan="100"> + <strong>Subtotal: </strong> + <t t-esc="p['subtotal']"></t> + </td> + </tr> + </t> + </template> + <template id="separator_template"> + <!-- Separator --> + <t t-if="'separator' in p['category'] and p['category'].separator is True"> + <tr class="text-center"> + <td colspan="100" class="active"> + *** + </td> + </tr> + </t> + </template> + </data> +</openerp> + |
