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 ().
+#
+# 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 .
+#
+##############################################################################
+
+{
+ '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 @@
+
+
+
+
+ Services
+
+
+
+ 1
+
+
+
+ Material
+
+
+
+ 10
+
+
+
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 ().
+#
+# 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 .
+#
+##############################################################################
+
+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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+ | Description |
+ Quantity |
+ Unit of measure |
+ Unit Price |
+ Discount (%) |
+ Taxes |
+ Amount |
+
+
+
+ ]]>
+
+
+
+
+
+
+
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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+ |
+
+
+ |
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+ | Description |
+ Taxes |
+ Quantity |
+ Unit Price |
+ Disc.(%) |
+ Price |
+
+
+
+ ]]>
+
+
+
+
+
+
+
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 @@
+
+
+
+
+
+ sale.order.form.inherit_1
+ sale.order
+
+
+
+
+
+
+
+
+
+
+
+ sale.order.line.form.inherit_2
+ sale.order
+
+
+
+
+
+
+
+
+
+
+
+
+ account.invoice.form.inherit_1
+ account.invoice
+
+
+
+
+
+
+
+
+
+
+
+ account.invoice.line.form.inherit_2
+ account.invoice.line
+
+
+
+
+
+
+
+
+
+
+
+
+ report.configuration.form.view
+ sale_layout.category
+
+
+
+
+
+
+ report.configuration.form.view
+ sale_layout.category
+
+
+
+
+
+
+
+
+
+
+
+
+ report.configuration.search.view
+ sale_layout.category
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Report Configuration
+ sale_layout.category
+ form
+ tree,form
+
+
+
+
+
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 @@
+
+
+
+
+
+
+
+ |
+
+
+
+
+ Uncategorized
+
+ |
+
+
+
+
+
+
+
+ |
+ Subtotal:
+
+ |
+
+
+
+
+
+
+
+ |
+ ***
+ |
+
+
+
+
+
+
|