[ADD] module account_invoice_reference which aims to simplify the "references" fields on the invoices.

This commit is contained in:
Yannick Vaucher
2014-05-23 11:33:58 +02:00
14 changed files with 671 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Guewen Baconnier
# Copyright 2014 Camptocamp 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import account_move

View File

@@ -0,0 +1,146 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Guewen Baconnier
# Copyright 2014 Camptocamp 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
{'name' : 'Invoices Reference',
'version' : '1.0',
'author' : 'Camptocamp',
'maintainer': 'Camptocamp',
'license': 'AGPL-3',
'category': 'category',
'complexity': "easy",
'depends' : ['account',
],
'description': """
Invoices Reference
==================
Aims to simplify the "references" fields on the invoices.
We observed difficulties for the users to file the references (name,
origin, free reference) and above all, to understand which field will be
copied in the reference field of the move and move lines.
The approach here is to state simple rules with one concern: consistency.
The reference of the move lines must be the number of the document at their very
origin (number of a sales order, of an external document like a supplier
invoice, ...). The goal is for the accountant to be able to trace to the
source document from a ledger).
The description of a line should always be... well, a description. Not a number
or a cryptic reference.
It particularly fits with other modules of the bank-statement-reconcile series
as account_advanced_reconcile_transaction_ref.
Fields
------
Enumerating the information we need in an invoice, we find that the
mandatory fields are:
* Invoice Number
* Description
* Internal Reference ("our reference")
* External Reference ("customer or supplier reference")
* Optionally, a technical transaction reference (credit card payment gateways, SEPA, ...)
Now, on the move lines:
* Name
* Reference
* Optionally, a technical transaction reference (added by the module `base_transaction_id`)
Let's see how the information will be organized with this module.
Customers Invoices / Refunds
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-----------------+-----------------+------------------------------+
| Information | Invoice field | Instead of (in base modules) |
+=================+=================+==============================+
| Invoice number | Invoice number | Invoice number |
+-----------------+-----------------+------------------------------+
| Description | Name | -- |
+-----------------+-----------------+------------------------------+
| Internal Ref | Origin | Origin |
+-----------------+-----------------+------------------------------+
| External Ref | Reference | Name |
+-----------------+-----------------+------------------------------+
Information propagated to the move lines:
+-----------------+------------------------------------+
| Move line field | Invoice field |
+=================+====================================+
| Description | Name |
+-----------------+------------------------------------+
| Reference | Origin, or Invoice number if empty |
+-----------------+------------------------------------+
Supplier Invoices / Refunds
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Supplier invoices have an additional field `supplier_invoice_number`
that we consider as redundant with the reference field. This field is kept
and even set as mandatory, while the reference field is hidden.
+-----------------+-----------------+------------------------------+
| Information | Invoice field | Instead of (in base modules) |
+=================+=================+==============================+
| Invoice number | Invoice number | Invoice number |
+-----------------+-----------------+------------------------------+
| Description | Name | -- |
+-----------------+-----------------+------------------------------+
| Internal Ref | Origin | Origin |
+-----------------+-----------------+------------------------------+
| External Ref | Supplier number | Supplier number |
+-----------------+-----------------+------------------------------+
The reference field is hidden when the reference type is "free reference",
because it is already filed in the Supplier invoice number.
Information propagated to the move lines:
+-----------------+---------------------------------------------+
| Move line field | Invoice field |
+=================+=============================================+
| Description | Name |
+-----------------+---------------------------------------------+
| Reference | Supplier number, or Invoice number if empty |
+-----------------+---------------------------------------------+
""",
'website': 'http://www.camptocamp.com',
'data': ['account_invoice_view.xml',
],
'test': ['test/out_invoice_with_origin.yml',
'test/out_invoice_without_origin.yml',
'test/in_invoice_with_supplier_number.yml',
'test/in_invoice_without_supplier_number.yml',
'test/out_refund_with_origin.yml',
'test/out_refund_without_origin.yml',
'test/in_refund_with_supplier_number.yml',
'test/in_refund_without_supplier_number.yml',
],
'installable': True,
'auto_install': False,
}

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<record id="invoice_supplier_form" model="ir.ui.view">
<field name="name">account.invoice.supplier.form</field>
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_supplier_form"/>
<field name="arch" type="xml">
<field name="supplier_invoice_number" position="attributes">
<attribute name="required">1</attribute>
</field>
<!-- remove the name to move it in a better place -->
<xpath expr="/form/sheet/notebook/page[@string='Other Info']//field[@name='name']"
position="replace">
</xpath>
<field name="reference_type" position="after">
<label string="Enter the reference in the Supplier Invoice Number field"
class="oe_inline"
attrs="{'invisible': [('reference_type', '!=', 'none')]}"/>
</field>
<field name="reference" position="attributes">
<attribute name="attrs">{'invisible': [('reference_type', '=', 'none')]}</attribute>
</field>
<xpath expr="//field[@name='reference_type']/parent::div" position="after">
<field name="name"/>
</xpath>
</field>
</record>
<record id="invoice_form" model="ir.ui.view">
<field name="name">account.invoice.form</field>
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_form"/>
<field name="arch" type="xml">
<xpath expr="//page[@string='Other Info']//field[@name='name']" position="replace">
<field name="reference" string="Customer Reference"/>
</xpath>
<field name="fiscal_position" position="after">
<field name="name"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Guewen Baconnier
# Copyright 2014 Camptocamp 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm, fields
class account_move(orm.Model):
_inherit = 'account.move'
def create(self, cr, uid, vals, context=None):
if context is None:
context = {}
# invoice from which the move is generated
invoice = context.get('invoice')
if invoice:
assert isinstance(invoice, orm.browse_record)
invoice_obj = self.pool['account.invoice']
ref = invoice_obj._ref_from_invoice(cr, uid, invoice, context=context)
vals = vals.copy()
vals['ref'] = ref
move_id = super(account_move, self).\
create(cr, uid, vals, context=context)
return move_id
class account_invoice(orm.Model):
_inherit = 'account.invoice'
def _ref_from_invoice(self, cr, uid, invoice, context=None):
if invoice.type in ('out_invoice', 'out_refund'):
return invoice.origin
elif invoice.type in ('in_invoice', 'in_refund'):
return invoice.supplier_invoice_number
def action_number(self, cr, uid, ids, context=None):
# force the number of the invoice to be updated for the
# subsequent browse
self.write(cr, uid, ids, {})
for invoice in self.browse(cr, uid, ids, context=context):
ref = self._ref_from_invoice(cr, uid, invoice, context=context)
if not ref:
ref = invoice.number
move_id = invoice.move_id.id if invoice.move_id else False
self.write(cr, uid, ids, {'internal_number': invoice.number},
context=context)
cr.execute('UPDATE account_move SET ref=%s '
'WHERE id=%s AND (ref is null OR ref = \'\')',
(ref, move_id))
cr.execute('UPDATE account_move_line SET ref=%s '
'WHERE move_id=%s AND (ref is null OR ref = \'\')',
(ref, move_id))
cr.execute('UPDATE account_analytic_line SET ref=%s '
'FROM account_move_line '
'WHERE account_move_line.move_id = %s '
'AND account_analytic_line.move_id = account_move_line.id',
(ref, move_id))
return True
def create(self, cr, uid, vals, context=None):
if (vals.get('supplier_invoice_reference') and not
vals.get('reference')):
vals['reference'] = vals['supplier_invoice_reference']
return super(account_invoice, self).create(cr, uid, vals,
context=context)
def write(self, cr, uid, ids, vals, context=None):
if vals.get('supplier_invoice_reference'):
if isinstance(ids, (int, long)):
ids = [ids]
for invoice in self.browse(cr, uid, ids, context=context):
local_vals = vals
if not invoice.reference:
locvals = vals.copy()
locvals['reference'] = vals['supplier_invoice_reference']
super(account_invoice, self).write(cr, uid, [invoice.id],
locvals, context=context)
return True
else:
return super(account_invoice, self).write(cr, uid, ids, vals,
context=context)

View File

@@ -0,0 +1,42 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * account_invoice_reference
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-01-24 14:43+0000\n"
"PO-Revision-Date: 2014-01-24 14:43+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: account_invoice_reference
#: view:account.invoice:0
msgid "Enter the reference in the Supplier Invoice Number field"
msgstr ""
#. module: account_invoice_reference
#: view:account.invoice:0
msgid "{'invisible': [('reference_type', '=', 'none')]}"
msgstr ""
#. module: account_invoice_reference
#: model:ir.model,name:account_invoice_reference.model_account_move
msgid "Account Entry"
msgstr ""
#. module: account_invoice_reference
#: view:account.invoice:0
msgid "Customer Reference"
msgstr ""
#. module: account_invoice_reference
#: model:ir.model,name:account_invoice_reference.model_account_invoice
msgid "Invoice"
msgstr ""

View File

@@ -0,0 +1,42 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * account_invoice_reference
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-01-24 14:43+0000\n"
"PO-Revision-Date: 2014-01-24 14:43+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: account_invoice_reference
#: view:account.invoice:0
msgid "Enter the reference in the Supplier Invoice Number field"
msgstr "Veuillez saisir la référence dans le champ Numéro de facture fournisseur"
#. module: account_invoice_reference
#: view:account.invoice:0
msgid "{'invisible': [('reference_type', '=', 'none')]}"
msgstr ""
#. module: account_invoice_reference
#: model:ir.model,name:account_invoice_reference.model_account_move
msgid "Account Entry"
msgstr "Pièce comptable"
#. module: account_invoice_reference
#: view:account.invoice:0
msgid "Customer Reference"
msgstr "Référence client"
#. module: account_invoice_reference
#: model:ir.model,name:account_invoice_reference.model_account_invoice
msgid "Invoice"
msgstr "Facture"

View File

@@ -0,0 +1,40 @@
-
In order to check if the reference of the move is the supplier invoice number of
the invoice (supplier invoices), I create an invoice
-
!record {model: account.invoice, id: invoice_reference_supplier_number, view: account.invoice_supplier_form}:
account_id: account.a_pay
check_total: 3000.0
company_id: base.main_company
currency_id: base.EUR
invoice_line:
- account_id: account.a_expense
name: '[PCSC234] PC Assemble SC234'
price_unit: 300.0
product_id: product.product_product_3
quantity: 10.0
uos_id: product.product_uom_unit
journal_id: account.expenses_journal
partner_id: base.res_partner_12
reference_type: none
supplier_invoice_number: ZZZ246
type: in_invoice
-
Set again the type of the invoice (not set on the first one...)
-
!record {model: account.invoice, id: invoice_reference_supplier_number, view: account.invoice_supplier_form}:
type: in_invoice
-
Ensure that the invoice is a supplier invoice
-
!assert {model: account.invoice, id: invoice_reference_supplier_number}:
- type == 'in_invoice'
-
I create invoice by clicking on Create button
-
!workflow {model: account.invoice, action: invoice_open, ref: invoice_reference_supplier_number}
-
I check that the reference of the move is the supplier invoice number of the invoice
-
!assert {model: account.invoice, id: invoice_reference_supplier_number}:
- move_id.ref == 'ZZZ246'

View File

@@ -0,0 +1,39 @@
-
In order to check if the reference of the move is the supplier invoice number of
the invoice (supplier invoices), I create an invoice
-
!record {model: account.invoice, id: invoice_reference_no_supplier_number, view: account.invoice_supplier_form}:
account_id: account.a_pay
check_total: 3000.0
company_id: base.main_company
currency_id: base.EUR
invoice_line:
- account_id: account.a_expense
name: '[PCSC234] PC Assemble SC234'
price_unit: 300.0
product_id: product.product_product_3
quantity: 10.0
uos_id: product.product_uom_unit
journal_id: account.expenses_journal
partner_id: base.res_partner_12
reference_type: none
type: in_invoice
-
Set again the type of the invoice (not set on the first one...)
-
!record {model: account.invoice, id: invoice_reference_supplier_number, view: account.invoice_supplier_form}:
type: in_invoice
-
Ensure that the invoice is a supplier invoice
-
!assert {model: account.invoice, id: invoice_reference_supplier_number}:
- type == 'in_invoice'
-
I create invoice by clicking on Create button
-
!workflow {model: account.invoice, action: invoice_open, ref: invoice_reference_no_supplier_number}
-
I check that the reference of the move is the number of the invoice
-
!assert {model: account.invoice, id: invoice_reference_no_supplier_number}:
- move_id.ref == number

View File

@@ -0,0 +1,40 @@
-
In order to check if the reference of the move is the supplier invoice number of
the refund (supplier refunds), I create a refund
-
!record {model: account.invoice, id: refund_reference_supplier_number, view: account.invoice_supplier_form}:
account_id: account.a_pay
check_total: 3000.0
company_id: base.main_company
currency_id: base.EUR
invoice_line:
- account_id: account.a_expense
name: '[PCSC234] PC Assemble SC234'
price_unit: 300.0
product_id: product.product_product_3
quantity: 10.0
uos_id: product.product_uom_unit
journal_id: account.expenses_journal
partner_id: base.res_partner_12
reference_type: none
supplier_invoice_number: RZZZ246
type: in_refund
-
Set again the type of the refund (not set on the first one...)
-
!record {model: account.invoice, id: refund_reference_supplier_number, view: account.invoice_supplier_form}:
type: in_refund
-
Ensure that the refund is a supplier refund
-
!assert {model: account.invoice, id: refund_reference_supplier_number}:
- type == 'in_refund'
-
I create refund by clicking on Create button
-
!workflow {model: account.invoice, action: invoice_open, ref: refund_reference_supplier_number}
-
I check that the reference of the move is the supplier refund number of the refund
-
!assert {model: account.invoice, id: refund_reference_supplier_number}:
- move_id.ref == 'RZZZ246'

View File

@@ -0,0 +1,39 @@
-
In order to check if the reference of the move is the supplier refund number of
the refund (supplier refunds), I create an refund
-
!record {model: account.invoice, id: refund_reference_no_supplier_number, view: account.invoice_supplier_form}:
account_id: account.a_pay
check_total: 3000.0
company_id: base.main_company
currency_id: base.EUR
invoice_line:
- account_id: account.a_expense
name: '[PCSC234] PC Assemble SC234'
price_unit: 300.0
product_id: product.product_product_3
quantity: 10.0
uos_id: product.product_uom_unit
journal_id: account.expenses_journal
partner_id: base.res_partner_12
reference_type: none
type: in_refund
-
Set again the type of the refund (not set on the first one...)
-
!record {model: account.invoice, id: refund_reference_supplier_number, view: account.invoice_supplier_form}:
type: in_refund
-
Ensure that the refund is a supplier refund
-
!assert {model: account.invoice, id: refund_reference_supplier_number}:
- type == 'in_refund'
-
I create refund by clicking on Create button
-
!workflow {model: account.invoice, action: invoice_open, ref: refund_reference_no_supplier_number}
-
I check that the reference of the move is the number of the refund
-
!assert {model: account.invoice, id: refund_reference_no_supplier_number}:
- move_id.ref == number

View File

@@ -0,0 +1,23 @@
-
In order to check if the reference of the move is the origin of
the invoice (customer invoices), I create an invoice
-
!record {model: account.invoice, id: invoice_reference_origin}:
payment_term: account.account_payment_term_advance
journal_id: account.sales_journal
partner_id: base.res_partner_3
reference_type: none
name: 'Test Customer Invoice'
origin: ABC123
invoice_line:
- product_id: product.product_product_5
quantity: 10.0
-
I create invoice by clicking on Create button
-
!workflow {model: account.invoice, action: invoice_open, ref: invoice_reference_origin}
-
I check that the reference of the move is the origin of the invoice
-
!assert {model: account.invoice, id: invoice_reference_origin}:
- move_id.ref == 'ABC123'

View File

@@ -0,0 +1,22 @@
-
In order to check if the reference of the move is the number of
the invoice (customer invoices) when it has no origin, I create an invoice
-
!record {model: account.invoice, id: invoice_reference_no_origin}:
payment_term: account.account_payment_term_advance
journal_id: account.sales_journal
partner_id: base.res_partner_3
reference_type: none
name: 'Test Customer Invoice no origin'
invoice_line:
- product_id: product.product_product_5
quantity: 10.0
-
I create invoice by clicking on Create button
-
!workflow {model: account.invoice, action: invoice_open, ref: invoice_reference_no_origin}
-
I check that the reference of the move is the number of the invoice
-
!assert {model: account.invoice, id: invoice_reference_no_origin}:
- move_id.ref == number

View File

@@ -0,0 +1,34 @@
-
In order to check if the reference of the move is the origin of
the refund (customer refunds), I create a refund
-
!record {model: account.invoice, id: refund_reference_origin, view: account.invoice_form}:
payment_term: account.account_payment_term_advance
journal_id: account.sales_journal
partner_id: base.res_partner_3
reference_type: none
name: 'Test Customer refund'
origin: RABC123
invoice_line:
- product_id: product.product_product_5
quantity: 10.0
type: out_refund
-
Set again the type of the invoice (not set on the first one...)
-
!record {model: account.invoice, id: refund_reference_origin, view: account.invoice_form}:
type: out_refund
-
Ensure that the invoice is a customer refund
-
!assert {model: account.invoice, id: refund_reference_origin}:
- type == 'out_refund'
-
I create a refund by clicking on Create button
-
!workflow {model: account.invoice, action: invoice_open, ref: refund_reference_origin}
-
I check that the reference of the move is the origin of the refund
-
!assert {model: account.invoice, id: refund_reference_origin}:
- move_id.ref == 'RABC123'

View File

@@ -0,0 +1,33 @@
-
In order to check if the reference of the move is the number of
the refund (customer refunds) when it has no origin, I create a refund
-
!record {model: account.invoice, id: refund_reference_no_origin}:
payment_term: account.account_payment_term_advance
journal_id: account.sales_journal
partner_id: base.res_partner_3
reference_type: none
name: 'Test Customer refund no origin'
invoice_line:
- product_id: product.product_product_5
quantity: 10.0
type: out_refund
-
Set again the type of the invoice (not set on the first one...)
-
!record {model: account.invoice, id: refund_reference_no_origin, view: account.invoice_form}:
type: out_refund
-
Ensure that the invoice is a customer refund
-
!assert {model: account.invoice, id: refund_reference_no_origin}:
- type == 'out_refund'
-
I create refund by clicking on Create button
-
!workflow {model: account.invoice, action: invoice_open, ref: refund_reference_no_origin}
-
I check that the reference of the move is the number of the refund
-
!assert {model: account.invoice, id: refund_reference_no_origin}:
- move_id.ref == number