[IMP] sale_timesheet: invoice from plan project
We want to invoice from the project overview. To do so, a button can help people: it is only visible when an order linked to task in that project has something to invoice Any sale order line, even the ones with no service product will be invoiced. However, the user will have to choose which SO (involved in the project) to invoice. Then the "invoice order" wizard takes over.
This commit is contained in:
@@ -29,6 +29,7 @@ have real delivered quantities in sales orders.
|
||||
'data/sale_timesheet_filters.xml',
|
||||
'wizard/project_task_assign_so_line_views.xml',
|
||||
'wizard/project_create_sale_order_views.xml',
|
||||
'wizard/project_create_invoice_views.xml',
|
||||
],
|
||||
'demo': [
|
||||
'data/sale_service_demo.xml',
|
||||
|
||||
@@ -311,13 +311,32 @@ class SaleTimesheetController(http.Controller):
|
||||
|
||||
def _plan_prepare_actions(self, projects):
|
||||
actions = []
|
||||
if len(projects) == 1 and not projects.sale_line_id and not projects.tasks.mapped('sale_line_id') and request.env.user.has_group('sales_team.group_sale_salesman'):
|
||||
actions.append({
|
||||
'label': _("Create a Sales Order"),
|
||||
'type': 'action',
|
||||
'action_id': 'sale_timesheet.project_project_action_multi_create_sale_order',
|
||||
'context': json.dumps({'active_id': projects.id}),
|
||||
})
|
||||
if len(projects) == 1:
|
||||
if request.env.user.has_group('sales_team.group_sale_salesman'):
|
||||
if not projects.sale_line_id and not projects.tasks.mapped('sale_line_id'):
|
||||
actions.append({
|
||||
'label': _("Create a Sales Order"),
|
||||
'type': 'action',
|
||||
'action_id': 'sale_timesheet.project_project_action_multi_create_sale_order',
|
||||
'context': json.dumps({'active_id': projects.id, 'active_model': 'project.project'}),
|
||||
})
|
||||
if request.env.user.has_group('sales_team.group_sale_salesman_all_leads'):
|
||||
sale_orders = projects.tasks.mapped('sale_line_id.order_id').filtered(lambda so: so.invoice_status == 'to invoice')
|
||||
if sale_orders:
|
||||
if len(sale_orders) == 1:
|
||||
actions.append({
|
||||
'label': _("Create Invoice"),
|
||||
'type': 'action',
|
||||
'action_id': 'sale.action_view_sale_advance_payment_inv',
|
||||
'context': json.dumps({'active_ids': sale_orders.ids, 'active_model': 'project.project'}),
|
||||
})
|
||||
else:
|
||||
actions.append({
|
||||
'label': _("Create Invoice"),
|
||||
'type': 'action',
|
||||
'action_id': 'sale_timesheet.project_project_action_multi_create_invoice',
|
||||
'context': json.dumps({'active_id': projects.id, 'active_model': 'project.project'}),
|
||||
})
|
||||
return actions
|
||||
|
||||
def _plan_get_stat_button(self, projects):
|
||||
|
||||
@@ -3,3 +3,4 @@
|
||||
|
||||
from . import project_task_assign_so_line
|
||||
from . import project_create_sale_order
|
||||
from . import project_create_invoice
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class ProjectCreateInvoice(models.TransientModel):
|
||||
_name = 'project.create.invoice'
|
||||
_description = "Create a Invoice from project"
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
result = super(ProjectCreateInvoice, self).default_get(fields)
|
||||
|
||||
active_model = self._context.get('active_model')
|
||||
if active_model != 'project.project':
|
||||
raise UserError(_('You can only apply this action from a project.'))
|
||||
|
||||
active_id = self._context.get('active_id')
|
||||
if 'project_id' in fields and active_id:
|
||||
result['project_id'] = active_id
|
||||
return result
|
||||
|
||||
project_id = fields.Many2one('project.project', "Project", help="Project to make billable", required=True)
|
||||
sale_order_id = fields.Many2one('sale.order', string="Choose the Sales Order to invoice", required=True)
|
||||
amount_to_invoice = fields.Monetary("Amount to invoice", compute='_compute_amount_to_invoice', currency_field='currency_id', help="Total amount to invoice on the sales order, including all items (services, stockables, expenses, ...)")
|
||||
currency_id = fields.Many2one(related='sale_order_id.currency_id', readonly=True)
|
||||
|
||||
@api.onchange('project_id')
|
||||
def _onchange_project_id(self):
|
||||
sale_orders = self.project_id.tasks.mapped('sale_line_id.order_id').filtered(lambda so: so.invoice_status == 'to invoice')
|
||||
return {
|
||||
'domain': {'sale_order_id': [('id', 'in', sale_orders.ids)]},
|
||||
}
|
||||
|
||||
@api.multi
|
||||
@api.depends('sale_order_id')
|
||||
def _compute_amount_to_invoice(self):
|
||||
for wizard in self:
|
||||
amount_untaxed = 0.0
|
||||
amount_tax = 0.0
|
||||
for line in wizard.sale_order_id.order_line.filtered(lambda sol: sol.invoice_status == 'to invoice'):
|
||||
amount_untaxed += line.price_reduce * line.qty_to_invoice
|
||||
amount_tax += line.price_tax
|
||||
wizard.amount_to_invoice = amount_untaxed + amount_tax
|
||||
|
||||
@api.multi
|
||||
def action_create_invoice(self):
|
||||
if not self.sale_order_id and self.sale_order_id.invoice_status != 'to invoice':
|
||||
raise UserError(_("The selected Sales Order should contain something to invoice."))
|
||||
action = self.env.ref('sale.action_view_sale_advance_payment_inv').read()[0]
|
||||
action['context'] = {
|
||||
'active_ids': self.sale_order_id.ids
|
||||
}
|
||||
return action
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="project_create_invoice_view_form" model="ir.ui.view">
|
||||
<field name="name">project.create.invoice.view.form</field>
|
||||
<field name="model">project.create.invoice</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Create Sales Order from Project">
|
||||
<group>
|
||||
<field name="project_id" readonly="1"/>
|
||||
<field name="sale_order_id" options="{'no_create_edit': True}" context="{'sale_show_partner_name': True}"/>
|
||||
<field name="amount_to_invoice"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button string="Create Invoice" type="object" name="action_create_invoice" class="oe_highlight"/>
|
||||
<button string="Cancel" special="cancel" type="object" class="btn btn-default oe_inline"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="project_project_action_multi_create_invoice" model="ir.actions.act_window">
|
||||
<field name="name">Create Invoice</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">project.create.invoice</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="project_create_invoice_view_form"/>
|
||||
<field name="target">new</field>
|
||||
<field name="multi" eval="False"/>
|
||||
<field name="groups_id" eval="[(4, ref('sales_team.group_sale_salesman_all_leads'))]"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user