[ADD] sale: merged sale_layout into sale

This commit is contained in:
Julien Louette
2016-03-25 11:34:25 +01:00
committed by David Monjoie
parent 91e8de0b05
commit a6b240b6bc
14 changed files with 366 additions and 30 deletions
+1
View File
@@ -3,6 +3,7 @@
import sale
import sale_analytic
import sale_layout
import sales_team
import res_partner
import wizard
+7 -1
View File
@@ -32,6 +32,8 @@ You can choose flexible invoicing methods:
* *On Delivery Order*: Invoices are generated from picking (delivery)
* *Before Delivery*: A Draft invoice is created and must be paid before delivery
With this module you can personnalize the sale order and invoice report with
separators, page-breaks or subtotals.
The Dashboard for the Sales Manager will include
------------------------------------------------
@@ -39,7 +41,7 @@ The Dashboard for the Sales Manager will include
* Monthly Turnover (Graph)
""",
'website': 'https://www.odoo.com/page/crm',
'depends': ['sales_team','account', 'procurement', 'report'],
'depends': ['sales_team', 'account', 'procurement', 'report'],
'data': [
'wizard/sale_make_invoice_advance.xml',
'security/sale_security.xml',
@@ -47,6 +49,7 @@ The Dashboard for the Sales Manager will include
'sale_sequence.xml',
'sale_report.xml',
'sale_data.xml',
'sale_layout_category_data.xml',
'sale_view.xml',
'sales_team_view.xml',
'res_partner_view.xml',
@@ -55,6 +58,9 @@ The Dashboard for the Sales Manager will include
'res_config_view.xml',
'views/report_saleorder.xml',
'views/sale.xml',
'views/report_invoice_layouted.xml',
'views/sale_layout_category_view.xml',
'views/sale_layout_template.xml',
'sales_team_dashboard.xml',
'sale_tip_data.xml',
],
+3 -3
View File
@@ -45,10 +45,10 @@ class sale_configuration(osv.TransientModel):
(0, 'Salespeople do not need to view margins when quoting'),
(1, 'Display margins on quotations and sales orders')
], "Margins"),
'module_sale_layout': fields.selection([
'group_sale_layout': fields.selection([
(0, 'Do not personalize sale orders and invoice reports'),
(1, 'Personnalize the sale orders and invoice report with separators, page-breaks or subtotals')
], "Sale Reports Layout"),
(1, 'Personalize the sale orders and invoice report with separators, page-breaks or subtotals')
], "Sale Reports Layout", implied_group='sale.group_sale_layout'),
'module_website_quote': fields.selection([
(0, 'Print quotes or send by email'),
(1, 'Send online quotations based on templates (advanced)')
+1 -1
View File
@@ -18,7 +18,7 @@
<field name="group_discount_per_so_line" widget="radio"/>
<field name="module_website_quote" widget="radio"/>
<field name="module_sale_margin" widget="radio"/>
<field name="module_sale_layout" widget="radio"/>
<field name="group_sale_layout" widget="radio"/>
<field name="auto_done_setting" widget="radio"/>
<label for="sale_pricelist_setting" string="Sale Price"/>
<div>
+47 -1
View File
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from itertools import groupby
from datetime import datetime, timedelta
from openerp import SUPERUSER_ID
from openerp import api, fields, models, _
@@ -420,11 +421,29 @@ class SaleOrder(models.Model):
})
order.project_id = analytic
@api.multi
def layout_categories(self):
"""
Returns this order lines ordered by sale_layout_category sequence. Used to render the report.
"""
self.ensure_one()
grouped_lines = []
for category, lines in groupby(self.order_line, lambda l: l.layout_category_id):
grouped_lines.append({
'name': category and category.name or 'Uncategorized',
'subtotal': category and category.subtotal,
'separator': category and category.separator,
'pagebreak': category and category.pagebreak,
'lines': list(lines)
})
return grouped_lines
class SaleOrderLine(models.Model):
_name = 'sale.order.line'
_description = 'Sales Order Line'
_order = 'order_id desc, sequence, id'
_order = 'order_id, layout_category_id, sequence, id'
@api.depends('state', 'product_uom_qty', 'qty_delivered', 'qty_to_invoice', 'qty_invoiced')
def _compute_invoice_status(self):
@@ -656,6 +675,10 @@ class SaleOrderLine(models.Model):
help="Number of days between the order confirmation and the shipping of the products to the customer", oldname="delay")
procurement_ids = fields.One2many('procurement.order', 'sale_line_id', string='Procurements')
layout_category_id = fields.Many2one('sale.layout_category', string='Section')
layout_category_sequence = fields.Integer(related='layout_category_id.sequence', string='Layout Sequence', store=True)
# Store is intentionally set in order to keep the "historic" order.
@api.multi
def _prepare_invoice_line(self, qty):
"""
@@ -684,6 +707,8 @@ class SaleOrderLine(models.Model):
'discount': self.discount,
'uom_id': self.product_uom.id,
'product_id': self.product_id.id or False,
'layout_category_id': self.layout_category_id and self.layout_category_id.id or False,
'product_id': self.product_id.id or False,
'invoice_line_tax_ids': [(6, 0, self.tax_id.ids)],
'account_analytic_id': self.order_id.project_id.id,
}
@@ -812,10 +837,31 @@ class AccountInvoice(models.Model):
order.message_post(body=_("Invoice %s paid") % (name))
return res
@api.multi
def layout_categories(self):
"""
Returns this sale order lines ordered by sale_layout_category sequence. Used to render the report.
"""
self.ensure_one()
grouped_lines = []
for category, lines in groupby(self.invoice_line_ids, lambda l: l.layout_category_id):
grouped_lines.append({
'name': category and category.name or 'Uncategorized',
'subtotal': category and category.subtotal,
'separator': category and category.separator,
'pagebreak': category and category.pagebreak,
'lines': list(lines)
})
return grouped_lines
class AccountInvoiceLine(models.Model):
_inherit = 'account.invoice.line'
_order = 'invoice_id, layout_category_id, sequence, id'
sale_line_ids = fields.Many2many('sale.order.line', 'sale_order_line_invoice_rel', 'invoice_line_id', 'order_line_id', string='Sale Order Lines', readonly=True, copy=False)
layout_category_id = fields.Many2one('sale.layout_category', string='Section')
layout_category_sequence = fields.Integer(related='layout_category_id.sequence', string='Layout Sequence', store=True, default=0)
class ProcurementOrder(models.Model):
+23
View File
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.osv import osv, fields
class SaleLayoutCategory(osv.Model):
_name = 'sale.layout_category'
_order = 'sequence, id'
_columns = {
'name': fields.char('Name', required=True, translate=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
}
+20
View File
@@ -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>
+18
View File
@@ -191,6 +191,7 @@
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom, 'company_id': parent.company_id}"
attrs="{'readonly': ['|', ('qty_invoiced', '&gt;', 0), ('procurement_ids', '!=', [])]}"
/>
<field name="layout_category_id" groups="sale.group_sale_layout"/>
<field name="invoice_status" invisible="1"/>
<field name="qty_to_invoice" invisible="1"/>
<field name="qty_delivered_updateable" invisible="1"/>
@@ -243,6 +244,7 @@
attrs="{'readonly': ['|', ('qty_invoiced', '&gt;', 0), ('procurement_ids', '!=', [])]}"
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom, 'company_id': parent.company_id}"
/>
<field name="layout_category_id" groups="sale.group_sale_layout"/>
<field name="name"/>
<field name="product_uom_qty"
string="Ordered Qty"
@@ -639,6 +641,22 @@
<xpath expr="//field[@name='user_id']" position="after">
<field name="team_id"/>
</xpath>
<xpath expr="//page/field[@name='invoice_line_ids']/tree/field[@name='product_id']" position="after">
<field name="layout_category_id" groups="sale.group_sale_layout"/>
</xpath>
</data>
</field>
</record>
<record model="ir.ui.view" id="account_invoice_line_form">
<field name="name">account.invoice.line.form</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="after">
<field name="layout_category_id" groups="sale.group_sale_layout"/>
</xpath>
</data>
</field>
</record>
+5
View File
@@ -35,3 +35,8 @@ access_product_group_res_partner_sale_manager,res_partner group_sale_manager,bas
access_account_move_line_salesman,account.move.line invoice,account.model_account_move_line,base.group_sale_salesman,1,0,0,0
access_sale_order_invoicing_payments,sale.order,model_sale_order,account.group_account_invoice,1,1,0,0
access_sale_order_line_invoicing_payments,sale.order.line,model_sale_order_line,account.group_account_invoice,1,1,0,0
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,0
report_layout_category_4,report_layout_category_4,model_sale_layout_category,base.group_sale_salesman_all_leads,1,1,1,0
report_layout_category_5,report_layout_category_5,model_sale_layout_category,account.group_account_invoice,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
35 access_account_move_line_salesman account.move.line invoice account.model_account_move_line base.group_sale_salesman 1 0 0 0
36 access_sale_order_invoicing_payments sale.order model_sale_order account.group_account_invoice 1 1 0 0
37 access_sale_order_line_invoicing_payments sale.order.line model_sale_order_line account.group_account_invoice 1 1 0 0
38 report_layout_category_1 report_layout_category_1 model_sale_layout_category base.group_sale_manager 1 1 1 1
39 report_layout_category_2 report_layout_category_2 model_sale_layout_category account.group_account_manager 1 1 1 1
40 report_layout_category_3 report_layout_category_3 model_sale_layout_category base.group_sale_salesman 1 1 1 0
41 report_layout_category_4 report_layout_category_4 model_sale_layout_category base.group_sale_salesman_all_leads 1 1 1 0
42 report_layout_category_5 report_layout_category_5 model_sale_layout_category account.group_account_invoice 1 0 0 0
+6
View File
@@ -24,6 +24,12 @@
<field name="users" eval="[(4, ref('base.user_root'))]"/>
</record>
<record id="sale.group_sale_layout" model="res.groups">
<field name="name">Personalize sale order and invoice report</field>
<field name="category_id" ref="base.module_category_hidden"/>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
</record>
<record id="group_delivery_invoice_address" model="res.groups">
<field name="name">Addresses in Sales Orders</field>
<field name="category_id" ref="base.module_category_hidden"/>
@@ -0,0 +1,85 @@
<?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.layout_categories()" t-as="layout_category">
<t t-if="layout_category_size > 1" groups="sale.group_sale_layout">
<tr class="active">
<td colspan="7" style="font-weight: bold; border-bottom: 1px solid black;">&amp;bull;
<t t-esc="layout_category['name']"/>
</td>
</tr>
</t>
<!-- Lines associated -->
<t t-foreach="layout_category['lines']" t-as="l">
<tr>
<td><span t-field="l.name"/></td>
<td class="text-right">
<span t-field="l.quantity"/>
<span t-field="l.uom_id" groups="product.group_uom"/>
</td>
<td class="text-right">
<span t-field="l.price_unit"/>
</td>
<td t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">
<span t-field="l.discount"/>
</td>
<td>
<span t-esc="', '.join(map(lambda x: x.name, l.invoice_line_tax_ids))"/>
</td>
<td class="text-right">
<span t-field="l.price_subtotal"
t-field-options='{"widget": "monetary", "display_currency": "o.currency_id"}'/>
</td>
</tr>
</t>
<t t-if="layout_category_size > 1 and layout_category['subtotal']" groups="sale.group_sale_layout">
<tr class="text-right">
<td colspan="6">
<strong>Subtotal: </strong>
<t t-set="subtotal" t-value="sum(line.price_subtotal for line in layout_category['lines'])"/>
<span t-esc="subtotal" t-esc-options='{"widget": "monetary", "display_currency": "o.currency_id"}'/>
</td>
</tr>
</t>
<t t-if="layout_category_size > 1 and layout_category['separator']" groups="sale.group_sale_layout">
<tr class="text-center">
<td colspan="6">
<t t-call="sale.layout_category_separator"/>
</td>
</tr>
</t>
<t t-if="layout_category_size > 1 and layout_category['pagebreak']" groups="sale.group_sale_layout">
<t t-if="layout_category_index &lt; layout_category_size - 1">
<![CDATA[
</tbody>
</table>
<p style="page-break-before:always;"> </p>
<table class="table table-condensed">
]]>
<thead>
<tr>
<th>Description</th>
<th class="text-right">Quantity</th>
<th class="text-right">Unit Price</th>
<th t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">Disc.(%)</th>
<th class="text-right">Taxes</th>
<th class="text-right">Tax Excluded Price</th>
</tr>
</thead>
<![CDATA[
<tbody>
]]>
</t>
</t>
</t>
</xpath>
</template>
</data>
</openerp>
+76 -24
View File
@@ -15,7 +15,7 @@
<p t-if="doc.partner_id.vat">VAT: <span t-field="doc.partner_id.vat"/></p>
<div t-if="doc.partner_shipping_id != doc.partner_invoice_id" class="mt8">
<strong>Shipping address:</strong>
<div t-field="doc.partner_shipping_id"
<div t-field="doc.partner_shipping_id"
t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true, "phone_icons": true}'/>
<p t-if="doc.partner_id.vat">VAT: <span t-field="doc.partner_id.vat"/></p>
</div>
@@ -67,29 +67,81 @@
</tr>
</thead>
<tbody class="sale_tbody">
<t t-foreach="doc.order_line" t-as="l">
<tr t-if="l.product_uom_qty">
<td>
<span t-field="l.name"/>
</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 t-if="display_discount" class="text-right" 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.description or x.name), l.tax_id))"/>
</td>
<td class="text-right">
<span t-field="l.price_subtotal"
t-field-options='{"widget": "monetary", "display_currency": "doc.pricelist_id.currency_id"}'/>
</td>
</tr>
<t t-foreach="doc.layout_categories()" t-as="layout_category">
<t t-if="layout_category_size > 1" groups="sale.group_sale_layout">
<tr class="active">
<td colspan="7" style="font-weight: bold; border-bottom: 1px solid black;">&amp;bull;
<t t-esc="layout_category['name']"/>
</td>
</tr>
</t>
<!-- Lines associated -->
<t t-foreach="layout_category['lines']" t-as="l">
<tr>
<td><span t-field="l.name"/></td>
<td class="text-right">
<span t-field="l.product_uom_qty"/>
<span t-field="l.product_uom" groups="product.group_uom"/>
</td>
<td class="text-right">
<span t-field="l.price_unit"/>
</td>
<td t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">
<span t-field="l.discount"/>
</td>
<td>
<span t-esc="', '.join(map(lambda x: (x.description or x.name), l.tax_id))"/>
</td>
<td class="text-right">
<span t-field="l.price_subtotal"
t-field-options='{"widget": "monetary", "display_currency": "doc.pricelist_id.currency_id"}'/>
</td>
</tr>
</t>
<t t-if="layout_category_size > 1 and layout_category['subtotal']" groups="sale.group_sale_layout">
<tr class="text-right">
<td colspan="6">
<strong>Subtotal: </strong>
<t t-set="subtotal" t-value="sum(line.price_subtotal for line in layout_category['lines'])"/>
<span t-esc="subtotal" t-esc-options='{"widget": "monetary", "display_currency": "doc.pricelist_id.currency_id"}'/>
</td>
</tr>
</t>
<t t-if="layout_category_size > 1 and layout_category['separator']" groups="sale.group_sale_layout">
<tr class="text-center">
<td colspan="6">
<t t-call="sale.layout_category_separator"/>
</td>
</tr>
</t>
<t t-if="layout_category_size > 1 and layout_category['pagebreak']" groups="sale.group_sale_layout">
<t t-if="layout_category_index &lt; layout_category_size - 1">
<![CDATA[
</tbody>
</table>
<p style="page-break-before:always;"> </p>
<table class="table table-condensed">
]]>
<thead>
<tr>
<th>Description</th>
<th class="text-right">Quantity</th>
<th class="text-right">Unit Price</th>
<th t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">Disc.(%)</th>
<th class="text-right">Taxes</th>
<th class="text-right">Tax Excluded Price</th>
</tr>
</thead>
<![CDATA[
<tbody>
]]>
</t>
</t>
</t>
</tbody>
</table>
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Report configuration -->
<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">
<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_sales_config"
sequence="2"
name="Report Layout Categories"
groups="sale.group_sale_layout"
/>
</data>
</openerp>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="layout_category_separator">
***
</template>
</odoo>