Merge lp:~yannick-buron/sale-wkfl/sale-wkfl into lp:~sale-core-editors/sale-wkfl/7.0

Proposed by YannickB (YOLO consulting)
Status: Work in progress
Proposed branch: lp:~yannick-buron/sale-wkfl/sale-wkfl
Merge into: lp:~sale-core-editors/sale-wkfl/7.0
Diff against target: 339 lines (+314/-0)
5 files modified
sale_advance_invoice/__init__.py (+23/-0)
sale_advance_invoice/__openerp__.py (+55/-0)
sale_advance_invoice/invoice.py (+63/-0)
sale_advance_invoice/sale.py (+156/-0)
sale_advance_invoice/sale_view.xml (+17/-0)
To merge this branch: bzr merge lp:~yannick-buron/sale-wkfl/sale-wkfl
Reviewer Review Type Date Requested Status
Pedro Manuel Baeza Needs Resubmitting
Leonardo Pistone Abstain
Review via email: mp+207468@code.launchpad.net

Commit message

Add sale_advance_invoice

Description of the change

Add the module sale_advance_invoice which allow to use some different behavior for the way advance invoice are managed in OpenERP.

Essentially, some of my customers were shocked by the negative amount in final invoice when OpenERP create the final invoice.

I created two new behavior :
-Progressive, the final invoice will only have the remaining amount, we don't touch the advance invoices.
-Complete final invoice, when we launch the final invoice we refund the advance invoice and reassign payment to the final invoice.

The complete final invoice feature was developed for the customer "Destock Meubles", a furniture distributor, one year ago. Due to the long time needed to deliver the furniture and the french law, they absolutely needed advance invoices and at the end complete invoice so I had to develop the feature. For the record, everything was automated and integrated with magentoerpconnect, which mean that the advance invoice and final invoice were created at the import order.

The code was still quite ugly at this time so I didn't merge it in community addons. But currently, another customer asked me to remove the deduction amount and so I saw the opportunity to introduce a simpler mode, progressive, clean the module and finally merge it with OCA.

I hope this will be useful to some people. I really think that negative amount is bad on the final invoice, and even illegal regarding some legislation like the french one.

To post a comment you must log in.
Revision history for this message
Pedro Manuel Baeza (pedro.baeza) wrote :

Hi, Yannick, I think that your functionality overlaps the one of the sale_order_partial_invoice module that is on account-invoicing repository. It is there because it can be considered as an "invoicing" action. I think that it's more suitable to improve this module, if there is room for it, instead of providing another one with a similar approach.

If I am wrong, please tell me. Until then, I'm going to set this as Disapprove.

Regards.

review: Disapprove
Revision history for this message
YannickB (YOLO consulting) (yannick-buron) wrote :

Hi Pedro,

Thanks you for remembering me this module, I forget it.

I tested sale_order_partial_invoice one month ago, and saw that it didn't implemented the behavior I needed.

sale_order_partial_invoice add the possibility to invoice based on the order line, while sale_advance_invoice improve the native wizard of OpenERP based on a percentage of the whole order.

It shall be tested, but I believe both are compatible. Now that you remember me this module, I also agree that they shall be merged into one module (Little disclaimer, maybe it'll take me some time if I have to do it myself, I need to move on other project beginning from tomorrow).

Regards,
Yannick.

Revision history for this message
Pedro Manuel Baeza (pedro.baeza) wrote :

If you point me in which functions/functionality must be extracted from your module and merge in the other, I can help you in the implementation.

Regards.

Revision history for this message
YannickB (YOLO consulting) (yannick-buron) wrote :

Thank you Pedro, that's very kind of you.

I think both module have very simple code. I just checked, it seems that they have no function in common so in theory you shall be able to just take all my code and put it in sale_order_partial_invoice.

My module works as follow, it alter the _prepare_invoice functions in advance invoice and final invoice so that, after super() the initial function, it check if we are in progressive or final complete mode and if yes it recompute the invoice lines.

Also, if final complete mode, when we create the final invoice, all linked invoice will be refund and payment attached to sale order. Then, when we validate an invoice, it check if there is payment in the sale.order and if yes it reconcile it to the new invoice.

Also don't hesitate if you see design improvement.

Revision history for this message
Leonardo Pistone (lepistone) wrote :

Thanks for your work, Yannick, and Pedro for reviewing.

please let us know if you think a merge of the modules would be feasible (in that case this MP should not 'Needs revirew' anymore, or instead you think this module can indeed go further.

Leo

review: Abstain
Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

Hi,
Based on the previous comments, I set the status of this proposal to "Work in progress" so it will go out of the reviewers' radar. Feel free to change the status to "Needs Review" if you think that this module should be merged as-is or when it is ready to land.
Thanks

Revision history for this message
Pedro Manuel Baeza (pedro.baeza) wrote :

This project is now hosted on https://github.com/OCA/sale-workflow. Please move your proposal there. This guide may help you https://github.com/OCA/maintainers-tools/wiki/How-to-move-a-Merge-Proposal-to-GitHub

review: Needs Resubmitting

Unmerged revisions

32. By yannick@yannick_buron.synerpgy.fr

Add sale_advance_invoice

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'sale_advance_invoice'
2=== added file 'sale_advance_invoice/__init__.py'
3--- sale_advance_invoice/__init__.py 1970-01-01 00:00:00 +0000
4+++ sale_advance_invoice/__init__.py 2014-02-20 15:23:38 +0000
5@@ -0,0 +1,23 @@
6+# -*- coding: utf-8 -*-
7+##############################################################################
8+#
9+# OpenERP, Open Source Management Solution
10+# Copyright (C) Yannick Buron.
11+#
12+# This program is free software: you can redistribute it and/or modify
13+# it under the terms of the GNU Affero General Public License as
14+# published by the Free Software Foundation, either version 3 of the
15+# License, or (at your option) any later version.
16+#
17+# This program is distributed in the hope that it will be useful,
18+# but WITHOUT ANY WARRANTY; without even the implied warranty of
19+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+# GNU Affero General Public License for more details.
21+#
22+# You should have received a copy of the GNU Affero General Public License
23+# along with this program. If not, see <http://www.gnu.org/licenses/>.
24+#
25+##############################################################################
26+
27+import sale
28+import invoice
29
30=== added file 'sale_advance_invoice/__openerp__.py'
31--- sale_advance_invoice/__openerp__.py 1970-01-01 00:00:00 +0000
32+++ sale_advance_invoice/__openerp__.py 2014-02-20 15:23:38 +0000
33@@ -0,0 +1,55 @@
34+# -*- coding: utf-8 -*-
35+
36+##############################################################################
37+#
38+# OpenERP, Open Source Management Solution
39+# Copyright (C) Yannick Buron
40+#
41+# This program is free software: you can redistribute it and/or modify
42+# it under the terms of the GNU Affero General Public License as
43+# published by the Free Software Foundation, either version 3 of the
44+# License, or (at your option) any later version.
45+#
46+# This program is distributed in the hope that it will be useful,
47+# but WITHOUT ANY WARRANTY; without even the implied warranty of
48+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
49+# GNU Affero General Public License for more details.
50+#
51+# You should have received a copy of the GNU Affero General Public License
52+# along with this program. If not, see <http://www.gnu.org/licenses/>.
53+#
54+##############################################################################
55+
56+
57+{
58+ 'name': 'Sale Advance Invoice',
59+ 'version': '1.0',
60+ 'category': 'Sales Management',
61+ 'complexity': "easy",
62+ 'description': """
63+Sale Advance Invoice
64+========================
65+
66+This module allow you, through a select field in sale.order, to change the OpenERP behavior when you create a percent advance invoice :
67+
68+-Deduce the advance amount in final invoice. This is the default behavior of OpenERP.
69+-Create advance invoices, then in final invoice we create a complete invoice, refund the advance invoices and reassign previous payment to final invoice.
70+-Create advance invoices, then the final invoice will only have the remaining amount to pay.
71+
72+Possible improvement: Make theses methods also work with fix amount.
73+ """,
74+ 'author': 'Yannick Buron',
75+ 'website': '',
76+ 'depends': ['base', 'sale'],
77+ 'init_xml': [],
78+ 'update_xml': [
79+ 'sale_view.xml'
80+ ],
81+ 'demo_xml': [],
82+ 'test': [],
83+ 'installable': True,
84+ 'application': False,
85+ 'auto_install': False,
86+ 'certificate': '',
87+ 'images': [],
88+}
89
90=== added file 'sale_advance_invoice/invoice.py'
91--- sale_advance_invoice/invoice.py 1970-01-01 00:00:00 +0000
92+++ sale_advance_invoice/invoice.py 2014-02-20 15:23:38 +0000
93@@ -0,0 +1,63 @@
94+# -*- coding: utf-8 -*-
95+##############################################################################
96+#
97+# Copyright (C) SYNERPGY Vivek.
98+#
99+# This program is free software: you can redistribute it and/or modify
100+# it under the terms of the GNU Affero General Public License as
101+# published by the Free Software Foundation, either version 3 of the
102+# License, or (at your option) any later version.
103+#
104+# This program is distributed in the hope that it will be useful,
105+# but WITHOUT ANY WARRANTY; without even the implied warranty of
106+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
107+# GNU Affero General Public License for more details.
108+#
109+# You should have received a copy of the GNU Affero General Public License
110+# along with this program. If not, see <http://www.gnu.org/licenses/>.
111+#
112+##############################################################################
113+
114+
115+from osv import osv, fields
116+
117+class account_invoice(osv.osv):
118+
119+ _inherit = 'account.invoice'
120+
121+ def action_move_create(self, cr, uid, ids, context=None):
122+
123+ res = super(account_invoice, self).action_move_create(cr, uid, ids)
124+ # Pay the invoice
125+ account_mov_line_obj = self.pool.get('account.move.line')
126+ reconcile_obj = self.pool.get('account.move.reconcile')
127+ voucher_obj = self.pool.get('account.voucher')
128+ sale_obj = self.pool.get('sale.order')
129+ for invoice in self.browse(cr, uid, ids):
130+ # Search the sale order related with the invoices.
131+ cr.execute("""
132+ SELECT order_id FROM sale_order_invoice_rel
133+ WHERE invoice_id = %s""", (invoice.id,))
134+ res = cr.fetchone()
135+ if res:
136+ sale_order_id = res[0]
137+ sale_order = sale_obj.browse(cr, uid, sale_order_id)
138+ # Find all the unreconciled payments.
139+ payments = [payment for payment in sale_order.payment_ids]
140+
141+ line_ids = account_mov_line_obj.search(cr, uid, [('move_id', '=', invoice.move_id.id)], order='date_maturity')
142+
143+ for payment in payments:
144+ if payment.reconcile_id:
145+ reconcile_obj.unlink(cr, uid, payment.reconcile_id.id)
146+ for line in account_mov_line_obj.browse(cr, uid, line_ids):
147+ if (line.debit >= payment.credit) and (line.account_id == payment.account_id):
148+ try:
149+ account_mov_line_obj.reconcile_partial(cr, uid, [line.id,
150+ payment.id], type='auto')
151+ # Unlink the payment from the sale order.
152+ sale_obj.write(cr, uid, [sale_order_id],
153+ {'payment_ids': [(3, payment.id, False)]})
154+ except Exception, e:
155+ _logger.error(str(e))
156+ break
157
158=== added file 'sale_advance_invoice/sale.py'
159--- sale_advance_invoice/sale.py 1970-01-01 00:00:00 +0000
160+++ sale_advance_invoice/sale.py 2014-02-20 15:23:38 +0000
161@@ -0,0 +1,156 @@
162+# -*- coding: utf-8 -*-
163+##############################################################################
164+#
165+# OpenERP, Open Source Management Solution
166+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
167+#
168+# This program is free software: you can redistribute it and/or modify
169+# it under the terms of the GNU Affero General Public License as
170+# published by the Free Software Foundation, either version 3 of the
171+# License, or (at your option) any later version.
172+#
173+# This program is distributed in the hope that it will be useful,
174+# but WITHOUT ANY WARRANTY; without even the implied warranty of
175+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
176+# GNU Affero General Public License for more details.
177+#
178+# You should have received a copy of the GNU Affero General Public License
179+# along with this program. If not, see <http://www.gnu.org/licenses/>.
180+#
181+##############################################################################
182+import time
183+
184+import netsvc
185+from osv import osv, fields
186+from openerp.tools.translate import _
187+from tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
188+
189+
190+class sale_order(osv.osv):
191+
192+ _inherit = 'sale.order'
193+ _columns = {
194+ 'advance_payment_method': fields.selection([
195+ ('regular', 'Standard method - Deduce advance invoice in final invoice'),
196+ ('progressive','Progressive - Simply invoice remaining amount in final invoice'),
197+ ('final_complete','Final complete - Make complete final invoice, Refund and reassign payment of advance invoice')],
198+ 'How do you want to manage advance invoices?', required=True),
199+ 'percent_invoiced': fields.float('Percent already invoiced'),
200+ 'payment_ids': fields.many2many('account.move.line', 'sale_payment_line_rel', 'sale_id', 'payment_id', 'Payments'),
201+ }
202+
203+ _defaults = {
204+ 'advance_payment_method': 'regular',
205+ 'percent_invoiced': 0.0,
206+ }
207+
208+ def copy(self, cr, uid, id, default=None, context=None):
209+ if not default:
210+ default = {}
211+ default.update({
212+ 'payment_ids': [],
213+ 'percent_invoiced': 0.0,
214+ })
215+ return super(sale_order, self).copy(cr, uid, id, default, context=context)
216+
217+
218+ def _prepare_invoice_lines(self, cr, uid, order, percent, context=None):
219+
220+ sale_obj = self.pool.get('sale.order')
221+ ir_property_obj = self.pool.get('ir.property')
222+ fiscal_obj = self.pool.get('account.fiscal.position')
223+
224+ prop = ir_property_obj.get(cr, uid,
225+ 'property_account_income_categ', 'product.category', context=context)
226+ prop_id = prop and prop.id or False
227+ fallback_account_id = fiscal_obj.map_account(cr, uid, order.fiscal_position or False, prop_id)
228+
229+ inv_lines = []
230+ for sale_line in order.order_line:
231+ inv_lines.append((0,0, {
232+ 'name': sale_line.name,
233+ 'origin': order.name,
234+ 'account_id': sale_line.product_id and (sale_line.product_id.property_account_income and sale_line.product_id.property_account_income.id or sale_line.product_id.categ_id.property_account_income_categ.id) or fallback_account_id,
235+ 'price_unit': sale_line.price_unit * percent / 100,
236+ 'quantity': sale_line.product_uom_qty,
237+ 'discount': sale_line.discount,
238+ 'product_id': sale_line.product_id.id,
239+ 'invoice_line_tax_id': [(6,0,[x.id for x in sale_line.tax_id])],
240+ 'account_analytic_id': order.project_id.id or False,
241+ }))
242+ return inv_lines
243+
244+
245+ def _prepare_invoice(self, cr, uid, order, lines, context=None):
246+
247+ inv_vals = super(sale_order, self)._prepare_invoice(cr, uid, order, lines, context=context)
248+
249+ sale_obj = self.pool.get('sale.order')
250+ ir_property_obj = self.pool.get('ir.property')
251+ fiscal_obj = self.pool.get('account.fiscal.position')
252+
253+
254+ if order.advance_payment_method in ['final_complete','progressive']:
255+ if order.advance_payment_method == 'final_complete':
256+ percent = 100
257+ else:
258+ percent = 100 - order.percent_invoiced
259+ inv_vals['invoice_line'] = self._prepare_invoice_lines(cr, uid, order, percent, context=context)
260+
261+ return inv_vals
262+
263+ def action_invoice_create(self, cr, uid, ids, grouped=False,
264+ states=['confirmed', 'done', 'exception'],
265+ date_invoice = False, context=None):
266+ if not context:
267+ context = {}
268+ active_ids = []
269+ if 'active_ids' in context:
270+ active_ids = context['active_ids']
271+ move_line_obj = self.pool.get('account.move.line')
272+ refund_obj = self.pool.get('account.invoice.refund')
273+ # Refund and link the payments with the sale order.
274+ for sale in self.browse(cr, uid, ids, context=context):
275+ if sale.advance_payment_method == 'final_complete':
276+ invoices = sale.invoice_ids
277+ paid_invoices = [invoice for invoice in invoices if invoice.state == 'paid']
278+ payment_ids = [payment.id for invoice in paid_invoices
279+ for payment in invoice.payment_ids]
280+ move_line_obj._remove_move_reconcile(cr, uid, move_ids=payment_ids, context=context)
281+ # Link the payment with the sale order.
282+ res2 = self.write(cr, uid, [sale.id], {'payment_ids': [(6, 0, payment_ids)]})
283+ # Search the journal
284+ journal_ids = [paid_invoices[0].journal_id.id] if paid_invoices else False
285+ if journal_ids:
286+ context['active_ids'] = [x.id for x in paid_invoices]
287+ refund_id = refund_obj.create(cr, uid, {'description':'Refund for final invoice','filter_refund':'cancel'}, context=context)
288+ refund_obj.compute_refund(cr, uid, [refund_id], mode='cancel', context=context)
289+ context['active_ids'] = active_ids
290+ res = super(sale_order, self).action_invoice_create(
291+ cr, uid, ids, grouped=grouped,
292+ states=states, date_invoice=date_invoice,
293+ context=context)
294+ return res
295+
296+class sale_advance_payment_inv(osv.osv_memory):
297+
298+ _inherit = "sale.advance.payment.inv"
299+
300+ def _prepare_advance_invoice_vals(self, cr, uid, ids, context=None):
301+ sale_obj = self.pool.get('sale.order')
302+ ir_property_obj = self.pool.get('ir.property')
303+ fiscal_obj = self.pool.get('account.fiscal.position')
304+ wizard = self.browse(cr, uid, ids[0], context)
305+
306+ result = super(sale_advance_payment_inv,self)._prepare_advance_invoice_vals(cr, uid, ids, context=context)
307+ res = []
308+ for vals in result:
309+ sale_id = vals[0]
310+ inv_values = vals[1]
311+ sale = sale_obj.browse(cr, uid, [sale_id], context=context)[0]
312+ if wizard.advance_payment_method == 'percentage' and sale.advance_payment_method in ['progressive','final_complete']:
313+ inv_values['invoice_line'] = sale_obj._prepare_invoice_lines(cr, uid, sale, wizard.amount, context=context)
314+ sale_obj.write(cr, uid, [sale_id], {'percent_invoiced': sale.percent_invoiced + wizard.amount}, context=context)
315+ res.append((sale_id, inv_values))
316+ return res
317+
318
319=== added file 'sale_advance_invoice/sale_view.xml'
320--- sale_advance_invoice/sale_view.xml 1970-01-01 00:00:00 +0000
321+++ sale_advance_invoice/sale_view.xml 2014-02-20 15:23:38 +0000
322@@ -0,0 +1,17 @@
323+<?xml version="1.0" encoding="utf-8"?>
324+<openerp>
325+ <data>
326+
327+ <record id="view_order_form_advance_invoice" model="ir.ui.view">
328+ <field name="name">sale.order.form.advance.invoice</field>
329+ <field name="model">sale.order</field>
330+ <field name="inherit_id" ref="sale.view_order_form"/>
331+ <field name="type">form</field>
332+ <field name="arch" type="xml">
333+ <field name="payment_term" position="before">
334+ <field name="advance_payment_method" attrs="{'readonly': [('state', 'not in', ['draft','sent'])]}"/>
335+ </field>
336+ </field>
337+ </record>
338+ </data>
339+</openerp>

Subscribers

People subscribed via source and target branches