[IMP] account: new way of auto-completion of vendor bills/credit notes
This adds the possibilty to select another account.invoice object in a vendor bill or credit note, to automatically copy its lines. When the purchase app is installed, the same field can be used for auto-completion and users can choose between vendor bills or purchase orders (preferably PO) Co-authored-by: dat@openerp.com
This commit is contained in:
committed by
qdp-odoo
co-authored by
parent
04c44c479d
commit
051dfea35e
@@ -286,6 +286,8 @@ class AccountInvoice(models.Model):
|
||||
partner_id = fields.Many2one('res.partner', string='Partner', change_default=True,
|
||||
required=True, readonly=True, states={'draft': [('readonly', False)]},
|
||||
track_visibility='always')
|
||||
vendor_bill_id = fields.Many2one('account.invoice', string='Vendor Bill',
|
||||
help="Auto-complete from a past bill.")
|
||||
payment_term_id = fields.Many2one('account.payment.term', string='Payment Terms', oldname='payment_term',
|
||||
readonly=True, states={'draft': [('readonly', False)]},
|
||||
help="If you use payment terms, the due date will be computed automatically at the generation "
|
||||
@@ -372,6 +374,20 @@ class AccountInvoice(models.Model):
|
||||
('number_uniq', 'unique(number, company_id, journal_id, type)', 'Invoice Number must be unique per Company!'),
|
||||
]
|
||||
|
||||
# Load all Vendor Bill lines
|
||||
@api.onchange('vendor_bill_id')
|
||||
def _onchange_vendor_bill(self):
|
||||
if not self.vendor_bill_id:
|
||||
return {}
|
||||
self.currency_id = self.vendor_bill_id.currency_id
|
||||
new_lines = self.env['account.invoice.line']
|
||||
for line in self.vendor_bill_id.invoice_line_ids:
|
||||
new_lines += new_lines.new(line._prepare_invoice_line())
|
||||
self.invoice_line_ids += new_lines
|
||||
self.payment_term_id = self.vendor_bill_id.payment_term_id
|
||||
self.vendor_bill_id = False
|
||||
return {}
|
||||
|
||||
def _get_seq_number_next_stuff(self):
|
||||
self.ensure_one()
|
||||
journal_sequence = self.journal_id.sequence_id
|
||||
@@ -1655,6 +1671,23 @@ class AccountInvoiceLine(models.Model):
|
||||
raise UserError(_('You can only delete an invoice line if the invoice is in draft state.'))
|
||||
return super(AccountInvoiceLine, self).unlink()
|
||||
|
||||
def _prepare_invoice_line(self):
|
||||
data = {
|
||||
'name': self.name,
|
||||
'origin': self.origin,
|
||||
'uom_id': self.uom_id.id,
|
||||
'product_id': self.product_id.id,
|
||||
'account_id': self.account_id.id,
|
||||
'price_unit': self.price_unit,
|
||||
'quantity': self.quantity,
|
||||
'discount': self.discount,
|
||||
'account_analytic_id': self.account_analytic_id.id,
|
||||
'analytic_tag_ids': self.analytic_tag_ids.ids,
|
||||
'invoice_line_tax_ids': self.invoice_line_tax_ids.ids
|
||||
}
|
||||
return data
|
||||
|
||||
|
||||
class AccountInvoiceTax(models.Model):
|
||||
_name = "account.invoice.tax"
|
||||
_description = "Invoice Tax"
|
||||
|
||||
@@ -238,7 +238,7 @@
|
||||
<label string="Bill " attrs="{'invisible': ['|',('state', '=', 'draft'), ('type','!=','in_invoice')]}"/>
|
||||
<label string="Credit Note " attrs="{'invisible': ['|',('state', '=', 'draft'), ('type','!=','in_refund')]}"/>
|
||||
<h1 class="mt0">
|
||||
<field name="number" class="oe_inline" attrs="{'invisible': [('state', '=', 'draft')]}"/>
|
||||
<field name="number" class="oe_inline" attrs="{'invisible': [('state', '=', 'draft')]}"/>
|
||||
<div attrs="{'invisible': [('sequence_number_next_prefix','=',False)]}">
|
||||
<field name="sequence_number_next_prefix" class="oe_inline"/>
|
||||
<field name="sequence_number_next" class="oe_inline"/>
|
||||
@@ -252,6 +252,10 @@
|
||||
context="{'default_customer': 0, 'search_default_supplier': 1, 'default_supplier': 1, 'default_company_type': 'company'}"
|
||||
domain="[('supplier', '=', True)]"/>
|
||||
<field name="reference" string="Vendor Reference"/>
|
||||
<field name="vendor_bill_id" attrs="{'invisible': [('state','not in',['draft'])]}"
|
||||
domain="[('partner_id','child_of', [partner_id]), ('state','in',('open','paid')), ('type','=','in_invoice')]"
|
||||
string="Auto-Complete" placeholder="Select an old vendor bill"
|
||||
options="{'no_create': True}" context="{'show_total_amount': True}"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="origin" attrs="{'invisible': [('origin', '=', False)]}"/>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
from . import account_invoice
|
||||
from . import purchase
|
||||
from . import purchase_bill
|
||||
from . import res_company
|
||||
from . import res_config_settings
|
||||
from . import res_partner
|
||||
|
||||
@@ -4,16 +4,14 @@
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.tools.float_utils import float_compare
|
||||
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
class AccountInvoice(models.Model):
|
||||
_inherit = 'account.invoice'
|
||||
|
||||
purchase_id = fields.Many2one(
|
||||
comodel_name='purchase.order',
|
||||
string='Add Purchase Order',
|
||||
readonly=True, states={'draft': [('readonly', False)]},
|
||||
help='Encoding help. When selected, the associated purchase order lines are added to the vendor bill. Several PO can be selected.'
|
||||
help='Load the vendor bill based on selected purchase order. Several PO can be selected.'
|
||||
)
|
||||
|
||||
@api.onchange('state', 'partner_id', 'invoice_line_ids')
|
||||
@@ -29,7 +27,7 @@ class AccountInvoice(models.Model):
|
||||
purchase_ids = self.invoice_line_ids.mapped('purchase_id').filtered(lambda r: r.order_line <= purchase_line_ids)
|
||||
|
||||
result['domain'] = {'purchase_id': [
|
||||
('invoice_status', '=', 'to invoice'),
|
||||
('invoice_status', 'in', ['to invoice', 'no']),
|
||||
('partner_id', 'child_of', self.partner_id.id),
|
||||
('id', 'not in', purchase_ids.ids),
|
||||
]}
|
||||
@@ -48,7 +46,7 @@ class AccountInvoice(models.Model):
|
||||
date = self.date_invoice
|
||||
data = {
|
||||
'purchase_line_id': line.id,
|
||||
'name': line.order_id.name+': '+line.name,
|
||||
'name': line.order_id.name + ': ' + line.name,
|
||||
'origin': line.order_id.origin,
|
||||
'uom_id': line.product_uom.id,
|
||||
'product_id': line.product_id.id,
|
||||
@@ -243,7 +241,7 @@ class AccountInvoice(models.Model):
|
||||
invoice = super(AccountInvoice, self).create(vals)
|
||||
purchase = invoice.invoice_line_ids.mapped('purchase_line_id.order_id')
|
||||
if purchase and not invoice.refund_invoice_id:
|
||||
message = _("This vendor bill has been created from: %s") % (",".join(["<a href=# data-oe-model=purchase.order data-oe-id="+str(order.id)+">"+order.name+"</a>" for order in purchase]))
|
||||
message = _("This vendor bill has been created from: %s") % (",".join(["<a href=# data-oe-model=purchase.order data-oe-id=" + str(order.id) + ">" + order.name + "</a>" for order in purchase]))
|
||||
invoice.message_post(body=message)
|
||||
return invoice
|
||||
|
||||
@@ -257,7 +255,7 @@ class AccountInvoice(models.Model):
|
||||
#To get all po reference when updating invoice line or adding purchase order reference from vendor bill.
|
||||
purchase = (purchase_old | purchase_new) - (purchase_old & purchase_new)
|
||||
if purchase:
|
||||
message = _("This vendor bill has been modified from: %s") % (",".join(["<a href=# data-oe-model=purchase.order data-oe-id="+str(order.id)+">"+order.name+"</a>" for order in purchase]))
|
||||
message = _("This vendor bill has been modified from: %s") % (",".join(["<a href=# data-oe-model=purchase.order data-oe-id=" + str(order.id) + ">" + order.name + "</a>" for order in purchase]))
|
||||
invoice.message_post(body=message)
|
||||
return result
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models, tools
|
||||
from odoo.tools import formatLang
|
||||
|
||||
class PurchaseBillUnion(models.Model):
|
||||
_name = 'purchase.bill.union'
|
||||
_auto = False
|
||||
_description = 'Bills & Purchases'
|
||||
_order = "purchase_order_id desc, vendor_bill_id desc"
|
||||
|
||||
name = fields.Char(string='Reference', readonly=True)
|
||||
reference = fields.Char(string='Source', readonly=True)
|
||||
partner_id = fields.Many2one('res.partner', string='Vendor', readonly=True)
|
||||
date = fields.Date(string='Date', readonly=True)
|
||||
amount = fields.Float(string='Amount', readonly=True)
|
||||
currency_id = fields.Many2one('res.currency', string='Currency', readonly=True)
|
||||
company_id = fields.Many2one('res.company', 'Company', readonly=True)
|
||||
vendor_bill_id = fields.Many2one('account.invoice', string='Vendor Bill', readonly=True)
|
||||
purchase_order_id = fields.Many2one('purchase.order', string='Purchase Order', readonly=True)
|
||||
|
||||
@api.model_cr
|
||||
def init(self):
|
||||
tools.drop_view_if_exists(self.env.cr, 'purchase_bill_union')
|
||||
self.env.cr.execute("""
|
||||
CREATE OR REPLACE VIEW purchase_bill_union AS (
|
||||
SELECT
|
||||
id, number as name, reference, partner_id, date, amount_untaxed as amount, currency_id, company_id,
|
||||
id as vendor_bill_id, NULL as purchase_order_id
|
||||
FROM account_invoice
|
||||
WHERE
|
||||
type='in_invoice' and state in ('open','paid','cancel')
|
||||
UNION
|
||||
SELECT
|
||||
-id, name, partner_ref, partner_id, date_order as date, amount_untaxed as amount, currency_id, company_id,
|
||||
NULL as vendor_bill_id, id as purchase_order_id
|
||||
FROM purchase_order
|
||||
WHERE
|
||||
state = 'purchase' AND
|
||||
invoice_status in ('to invoice', 'no')
|
||||
)""")
|
||||
|
||||
def name_get(self):
|
||||
result = []
|
||||
for doc in self:
|
||||
name = doc.name or ''
|
||||
if doc.reference:
|
||||
name += ' - ' + doc.reference
|
||||
amount = doc.amount
|
||||
if doc.purchase_order_id and doc.purchase_order_id.invoice_status == 'no':
|
||||
amount = 0.0
|
||||
name += ': ' + formatLang(self.env, amount, monetary=True, currency_obj=doc.currency_id)
|
||||
result.append((doc.id, name))
|
||||
return result
|
||||
|
||||
|
||||
class AccountInvoice(models.Model):
|
||||
_inherit = 'account.invoice'
|
||||
|
||||
vendor_bill_purchase_id = fields.Many2one(
|
||||
comodel_name='purchase.bill.union',
|
||||
string='Auto-Complete'
|
||||
)
|
||||
|
||||
@api.onchange('vendor_bill_purchase_id')
|
||||
def _onchange_bill_purchase_order(self):
|
||||
if not self.vendor_bill_purchase_id:
|
||||
return {}
|
||||
self.purchase_id = self.vendor_bill_purchase_id.purchase_order_id
|
||||
self.vendor_bill_id = self.vendor_bill_purchase_id.vendor_bill_id
|
||||
self.vendor_bill_purchase_id = False
|
||||
return {}
|
||||
@@ -51,4 +51,5 @@ access_stock_location_purchase_manager,stock.location purchase manager,stock.mod
|
||||
access_stock_warehouse_orderpoint_manager,stock.warehouse.orderpoint,stock.model_stock_warehouse_orderpoint,purchase.group_purchase_manager,1,0,0,0
|
||||
access_stock_warehouse_orderpoint_user,stock.warehouse.orderpoint,stock.model_stock_warehouse_orderpoint,group_purchase_user,1,0,0,0
|
||||
access_product_price_history_purchase_user,prices.history purchase user,product.model_product_price_history,purchase.group_purchase_user,1,0,0,0
|
||||
access_product_price_history_purchase_manager,prices.history purchase manager,product.model_product_price_history,purchase.group_purchase_manager,1,1,1,1
|
||||
access_product_price_history_purchase_manager,prices.history purchase manager,product.model_product_price_history,purchase.group_purchase_manager,1,1,1,1
|
||||
access_purchase_bill_union,access_purchase_bill_union,model_purchase_bill_union,purchase.group_purchase_user,1,0,0,0
|
||||
|
||||
|
@@ -69,5 +69,11 @@
|
||||
<field name="domain_force">['|',('order_id.message_partner_ids','child_of',[user.commercial_partner_id.id]),('order_id.partner_id','child_of',[user.commercial_partner_id.id])]</field>
|
||||
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="purchase_bill_union_comp_rule">
|
||||
<field name="name">Purchases & Bills Union multi-company</field>
|
||||
<field name="model_id" ref="model_purchase_bill_union"/>
|
||||
<field name="global" eval="True"/>
|
||||
<field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -56,10 +56,18 @@
|
||||
<field name="name">account.invoice.supplier.purchase</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="inherit_id" ref="account.invoice_supplier_form"/>
|
||||
<field name="groups_id" eval="[(4,ref('purchase.group_purchase_user'))]"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="reference" position="after" >
|
||||
<field name="purchase_id" attrs="{'readonly': [('state','not in',['draft'])], 'invisible': ['|', ('state', '=', 'purchase'), ('type', '=', 'in_refund')]}" class="oe_edit_only"
|
||||
options="{'no_create': True}" context="{'show_total_amount': True}"/>
|
||||
<field name="vendor_bill_id" position="after">
|
||||
<field name="vendor_bill_purchase_id" attrs="{'invisible': ['|', '|', ('state','not in',['draft']), ('state', '=', 'purchase'), ('type', '=', 'in_refund')]}"
|
||||
class="oe_edit_only" domain="[('partner_id','child_of',[partner_id])]"
|
||||
placeholder="Select a purchase order or an old bill"
|
||||
options="{'no_create': True, 'no_open': True}"/>
|
||||
<field name="purchase_id" class="oe_edit_only"
|
||||
invisible="1" context="{'show_total_amount': True}"/>
|
||||
</field>
|
||||
<field name="vendor_bill_id" position="attributes">
|
||||
<attribute name="attrs">{'invisible': ['|', ('type', '=', 'in_invoice'), ('state', 'not in', ['draft'])]}</attribute>
|
||||
</field>
|
||||
<xpath expr="//field[@name='invoice_line_ids']/tree/field[@name='company_id']" position="after">
|
||||
<field name="purchase_line_id" invisible="1" readonly="0"/>
|
||||
@@ -78,4 +86,37 @@
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_purchase_bill_union_filter" model="ir.ui.view">
|
||||
<field name="name">purchase.bill.union.select</field>
|
||||
<field name="model">purchase.bill.union</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Reference Document">
|
||||
<field name="name" string="Reference" filter_domain="['|', ('name','ilike',self), ('reference','=like',str(self)+'%')]"/>
|
||||
<field name="amount"/>
|
||||
<separator/>
|
||||
<field name="partner_id" operator="child_of"/>
|
||||
<separator/>
|
||||
<filter name="purchase_orders" string="Purchase Orders" domain="[('purchase_order_id', '!=', False)]"/>
|
||||
<filter name="vendor_bills" string="Vendor Bills" domain="[('vendor_bill_id', '!=', False)]"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_purchase_bill_union_tree" model="ir.ui.view">
|
||||
<field name="name">purchase.bill.union.tree</field>
|
||||
<field name="model">purchase.bill.union</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Reference Document">
|
||||
<field name="name"/>
|
||||
<field name="reference"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="date"/>
|
||||
<field name="amount"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user