Merge lp:~fabien-morin/unifield-wm/fm-us-713 into lp:unifield-wm

Proposed by jftempo
Status: Merged
Merged at revision: 2736
Proposed branch: lp:~fabien-morin/unifield-wm/fm-us-713
Merge into: lp:unifield-wm
Diff against target: 1243 lines (+1038/-50)
8 files modified
account_override/invoice.py (+1/-14)
analytic_distribution/wizard/analytic_distribution_wizard.py (+70/-4)
register_accounting/__init__.py (+1/-0)
register_accounting/__openerp__.py (+1/-0)
register_accounting/account_bank_statement.py (+122/-27)
register_accounting/account_direct_invoice_wizard.py (+729/-0)
register_accounting/account_invoice_view.xml (+2/-5)
register_accounting/wizard/account_direct_invoice_wizard_view.xml (+112/-0)
To merge this branch: bzr merge lp:~fabien-morin/unifield-wm/fm-us-713
Reviewer Review Type Date Requested Status
UniField Reviewer Team Pending
Review via email: mp+283064@code.launchpad.net
To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'account_override/invoice.py'
2--- account_override/invoice.py 2015-09-01 13:02:09 +0000
3+++ account_override/invoice.py 2016-01-19 10:37:47 +0000
4@@ -1250,20 +1250,7 @@
5 sequence = invoice.sequence_id
6 line = sequence.get_id(code_or_id='id', context=context)
7 vals.update({'line_number': line})
8- res = super(account_invoice_line, self).create(cr, uid, vals, context)
9- if vals.get('invoice_id', False):
10- inv_obj_name = 'account.invoice'
11- if self._name != 'account.invoice.line':
12- inv_obj_name = 'wizard.account.invoice'
13- invoice = self.pool.get(inv_obj_name).browse(cr, uid, vals.get('invoice_id'))
14- if invoice and invoice.is_direct_invoice and invoice.state == 'draft':
15- amount = 0.0
16- for l in invoice.invoice_line:
17- amount += l.price_subtotal
18- amount += vals.get('price_unit', 0.0) * vals.get('quantity', 0.0)
19- self.pool.get('account.invoice').write(cr, uid, [invoice.id], {'check_total': amount}, context)
20- self.pool.get('account.bank.statement.line').write(cr, uid, [x.id for x in invoice.register_line_ids], {'amount': -1 * amount}, context)
21- return res
22+ return super(account_invoice_line, self).create(cr, uid, vals, context)
23
24 def write(self, cr, uid, ids, vals, context=None):
25 """
26
27=== modified file 'analytic_distribution/wizard/analytic_distribution_wizard.py'
28--- analytic_distribution/wizard/analytic_distribution_wizard.py 2015-11-20 09:47:13 +0000
29+++ analytic_distribution/wizard/analytic_distribution_wizard.py 2016-01-19 10:37:47 +0000
30@@ -553,6 +553,10 @@
31 res[wiz.id] = True
32 elif wiz.direct_invoice_line_id and wiz.direct_invoice_line_id.invoice_id and wiz.direct_invoice_line_id.invoice_id.analytic_distribution_id:
33 res[wiz.id] = True
34+ elif wiz.account_direct_invoice_wizard_line_id and\
35+ wiz.account_direct_invoice_wizard_line_id.invoice_wizard_id and\
36+ wiz.account_direct_invoice_wizard_line_id.invoice_wizard_id.analytic_distribution_id:
37+ res[wiz.id] = True
38 elif wiz.commitment_line_id and wiz.commitment_line_id.commit_id and wiz.commitment_line_id.commit_id.analytic_distribution_id:
39 res[wiz.id] = True
40 elif wiz.cash_return_line_id and wiz.cash_return_line_id.wizard_id and wiz.cash_return_line_id.wizard_id.analytic_distribution_id:
41@@ -615,6 +619,8 @@
42 help="This account come from an invoice line. When filled in it permits to test compatibility for each funding pool and display those that was linked with."),
43 'direct_invoice_id': fields.many2one('wizard.account.invoice', string="Direct Invoice"),
44 'direct_invoice_line_id': fields.many2one('wizard.account.invoice.line', string="Direct Invoice Line"),
45+ 'account_direct_invoice_wizard_id': fields.many2one('account.direct.invoice.wizard', string="Direct Invoice Wizard"),
46+ 'account_direct_invoice_wizard_line_id': fields.many2one('account.direct.invoice.wizard.line', string="Direct Invoice Wizard Line"),
47 'sale_order_id': fields.many2one('sale.order', string="Sale Order"),
48 'sale_order_line_id': fields.many2one('sale.order.line', string="Sale Order Line"),
49 'amount': fields.function(_get_amount, method=True, string="Total amount", type="float", readonly=True),
50@@ -1047,7 +1053,9 @@
51 ('direct_invoice_line_id', 'wizard.account.invoice.line'), ('commitment_id', 'account.commitment'),
52 ('commitment_line_id', 'account.commitment.line'), ('model_id', 'account.model'), ('model_line_id', 'account.model.line'),
53 ('accrual_line_id', 'msf.accrual.line'), ('sale_order_id', 'sale.order'), ('sale_order_line_id', 'sale.order.line'), ('move_id', 'account.move'),
54- ('cash_return_id', 'wizard.cash.return'), ('cash_return_line_id', 'wizard.advance.line')]:
55+ ('cash_return_id', 'wizard.cash.return'), ('cash_return_line_id', 'wizard.advance.line'),
56+ ('account_direct_invoice_wizard_id','account.direct.invoice.wizard'),
57+ ('account_direct_invoice_wizard_line_id','account.direct.invoice.wizard.line'),]:
58 if getattr(wiz, el[0], False):
59 obj_id = getattr(wiz, el[0], False).id
60 self.pool.get(el[1]).write(cr, uid, [obj_id], {'analytic_distribution_id': distrib_id}, context=context)
61@@ -1093,6 +1101,31 @@
62 'res_id': direct_invoice_id,
63 'context': context,
64 }
65+ if wiz and (wiz.account_direct_invoice_wizard_id or wiz.account_direct_invoice_wizard_line_id):
66+ # Get direct_invoice id
67+ direct_invoice_id = (wiz.account_direct_invoice_wizard_id and wiz.account_direct_invoice_wizard_id.id) or\
68+ (wiz.account_direct_invoice_wizard_line_id and\
69+ wiz.account_direct_invoice_wizard_line_id.invoice_wizard_id.id) or False
70+ # Get register from which we come from
71+ direct_invoice = self.pool.get('account.direct.invoice.wizard').browse(cr, uid, [direct_invoice_id], context=context)[0]
72+ register_id = direct_invoice and direct_invoice.register_id and direct_invoice.register_id.id or False
73+ if register_id:
74+ context.update({
75+ 'active_id': register_id,
76+ 'type': 'in_invoice',
77+ 'journal_type': 'purchase',
78+ 'active_ids': register_id,
79+ })
80+ return {
81+ 'name': "Supplier Direct Invoice",
82+ 'type': 'ir.actions.act_window',
83+ 'res_model': 'account.direct.invoice.wizard',
84+ 'target': 'new',
85+ 'view_mode': 'form',
86+ 'view_type': 'form',
87+ 'res_id': direct_invoice_id,
88+ 'context': context,
89+ }
90 wizard_account_invoice = self._check_open_wizard_account_invoice(cr, uid, wiz, context)
91 if wizard_account_invoice:
92 return wizard_account_invoice
93@@ -1252,11 +1285,11 @@
94 # Take distribution from invoice if we come from an invoice line
95 if wiz.invoice_line_id:
96 il = wiz.invoice_line_id
97- distrib = il.invoice_id and il.invoice_id.analytic_distribution_id and il.invoice_id.analytic_distribution_id or False
98+ distrib = il.invoice_id and il.invoice_id.analytic_distribution_id or False
99 # Same thing for purchase order line
100 elif wiz.purchase_line_id:
101 pl = wiz.purchase_line_id
102- distrib = pl.order_id and pl.order_id.analytic_distribution_id and pl.order_id.analytic_distribution_id or False
103+ distrib = pl.order_id and pl.order_id.analytic_distribution_id or False
104 elif wiz.commitment_line_id:
105 pl = wiz.commitment_line_id
106 distrib = pl.commit_id and pl.commit_id.analytic_distribution_id or False
107@@ -1265,7 +1298,11 @@
108 distrib = pl.model_id and pl.model_id.analytic_distribution_id or False
109 elif wiz.direct_invoice_line_id:
110 il = wiz.direct_invoice_line_id
111- distrib = il.invoice_id and il.invoice_id.analytic_distribution_id and il.invoice_id.analytic_distribution_id or False
112+ distrib = il.invoice_id and il.invoice_id.analytic_distribution_id or False
113+ elif wiz.account_direct_invoice_wizard_line_id:
114+ il = wiz.account_direct_invoice_wizard_line_id
115+ distrib = il.invoice_wizard_id and \
116+ il.invoice_wizard_id.analytic_distribution_id or False
117 elif wiz.cash_return_line_id:
118 crl = wiz.cash_return_line_id
119 distrib = crl.wizard_id.analytic_distribution_id or False
120@@ -1336,6 +1373,35 @@
121 'res_id': direct_invoice_id,
122 'context': context,
123 }
124+ if wiz and (wiz.account_direct_invoice_wizard_id or wiz.account_direct_invoice_wizard_line_id):
125+ # Get direct_invoice id
126+ direct_invoice_id = (wiz.account_direct_invoice_wizard_id and \
127+ wiz.account_direct_invoice_wizard_id.id) or\
128+ (wiz.account_direct_invoice_wizard_line_id and\
129+ wiz.account_direct_invoice_wizard_line_id.invoice_wizard_id.id) or False
130+ # Get register from which we come from
131+ direct_invoice = self.pool.get('account.direct.invoice.wizard').browse(cr,
132+ uid, [direct_invoice_id], context=context)[0]
133+ register_id = direct_invoice and direct_invoice.register_id and direct_invoice.register_id.id or False
134+ if register_id:
135+ context.update({
136+ 'active_id': register_id,
137+ 'active_ids': register_id,
138+ })
139+ context.update({
140+ 'type': 'in_invoice',
141+ 'journal_type': 'purchase',
142+ })
143+ return {
144+ 'name': "Supplier Direct Invoice",
145+ 'type': 'ir.actions.act_window',
146+ 'res_model': 'account.direct.invoice.wizard',
147+ 'target': 'new',
148+ 'view_mode': 'form',
149+ 'view_type': 'form',
150+ 'res_id': direct_invoice_id,
151+ 'context': context,
152+ }
153 wizard_account_invoice = self._check_open_wizard_account_invoice(cr, uid, wiz, context)
154 if wizard_account_invoice:
155 return wizard_account_invoice
156
157=== modified file 'register_accounting/__init__.py'
158--- register_accounting/__init__.py 2014-03-07 11:05:37 +0000
159+++ register_accounting/__init__.py 2016-01-19 10:37:47 +0000
160@@ -32,6 +32,7 @@
161 import purchase
162 import report
163 import account_analytic_line
164+import account_direct_invoice_wizard
165
166
167
168
169=== modified file 'register_accounting/__openerp__.py'
170--- register_accounting/__openerp__.py 2015-08-14 12:46:42 +0000
171+++ register_accounting/__openerp__.py 2016-01-19 10:37:47 +0000
172@@ -69,6 +69,7 @@
173 'register_accounting_report.xml',
174 'account_wizard.xml',
175 'wizard/wizard_register_import.xml',
176+ 'wizard/account_direct_invoice_wizard_view.xml',
177 ],
178 "demo_xml" : [],
179 "test": [
180
181=== modified file 'register_accounting/account_bank_statement.py'
182--- register_accounting/account_bank_statement.py 2015-11-19 17:09:19 +0000
183+++ register_accounting/account_bank_statement.py 2016-01-19 10:37:47 +0000
184@@ -2454,46 +2454,142 @@
185 """
186 Open the attached invoice
187 """
188+ # Some verifications
189+ if not context:
190+ context = {}
191+ if isinstance(ids, (int, long)):
192+ ids = [ids]
193+
194+ # special processing for cash_return
195 cash_return = False
196 for st_line in self.browse(cr, uid, ids, context=context):
197 if not st_line.invoice_id:
198 raise osv.except_osv(_('Warning'), _('No invoice founded.'))
199 if st_line.from_cash_return:
200 cash_return = True
201- invoice = self.browse(cr, uid, ids[0], context=context).invoice_id
202- view_name = 'direct_supplier_invoice_form'
203- name = _('Supplier Direct Invoice')
204+ break
205 if cash_return:
206+ invoice = self.browse(cr, uid, ids[0], context=context).invoice_id
207 view_name = 'invoice_supplier_form_2'
208 name = _('Supplier Invoice')
209- # Search the customized view we made for Supplier Invoice (for * Register's users)
210- irmd_obj = self.pool.get('ir.model.data')
211- view_ids = irmd_obj.search(cr, uid, [('name', '=', view_name), ('model', '=', 'ir.ui.view')])
212- # Préparation de l'élément permettant de trouver la vue à afficher
213- if view_ids:
214- view = irmd_obj.read(cr, uid, view_ids[0])
215- view_id = (view.get('res_id'), view.get('name'))
216- else:
217- raise osv.except_osv(_('Error'), _("View not found."))
218- context.update({
219- 'active_id': ids[0],
220+ # Search the customized view we made for Supplier Invoice (for * Register's users)
221+ irmd_obj = self.pool.get('ir.model.data')
222+ view_ids = irmd_obj.search(cr, uid, [('name', '=', view_name), ('model', '=', 'ir.ui.view')])
223+ # Préparation de l'élément permettant de trouver la vue à afficher
224+ if view_ids:
225+ view = irmd_obj.read(cr, uid, view_ids[0])
226+ view_id = (view.get('res_id'), view.get('name'))
227+ else:
228+ raise osv.except_osv(_('Error'), _("View not found."))
229+ context.update({
230+ 'active_id': ids[0],
231+ 'type': invoice.type,
232+ 'journal_type': invoice.journal_id.type,
233+ 'active_ids': ids,
234+ 'from_register': True,
235+ })
236+ return {
237+ 'name': name,
238+ 'type': 'ir.actions.act_window',
239+ 'res_model': 'account.invoice',
240+ 'target': 'new',
241+ 'view_mode': 'form',
242+ 'view_type': 'form',
243+ 'view_id': view_id,
244+ 'res_id': invoice.id,
245+ 'context': context,
246+ }
247+
248+ ## Direct Invoice processing using temp objects
249+
250+ # Prepare some values
251+ invoice = self.browse(cr, uid, ids[0], context=context).invoice_id
252+
253+ # copy the analytic_distribution not to modify the original one
254+ analytic_distribution = self.pool.get('analytic.distribution')
255+ new_ad_id = None
256+ if invoice.analytic_distribution_id:
257+ new_ad_id = analytic_distribution.copy(cr, uid,
258+ invoice.analytic_distribution_id.id, context=context)
259+
260+ create_date = time.strftime('%Y-%m-%d %H:%M:%S')
261+
262+ # Prepare values for wizard
263+ vals = {
264+ 'account_id': invoice.account_id.id,
265+ 'address_invoice_id': invoice.address_invoice_id.id,
266+ 'address_contact_id': invoice.address_contact_id.id,
267+ 'amount_total': invoice.amount_total,
268+ 'analytic_distribution_id': new_ad_id,
269+ 'comment': invoice.comment,
270+ 'company_id': invoice.company_id.id,
271+ 'create_date': create_date,
272+ 'currency_id': invoice.currency_id.id,
273+ 'date_invoice': invoice.date_invoice,
274+ 'document_date': invoice.document_date,
275+ 'is_direct_invoice': invoice.is_direct_invoice,
276+ 'journal_id': invoice.journal_id.id,
277+ 'name': invoice.name,
278+ 'number': invoice.number,
279+ 'origin': invoice.origin,
280+ 'original_invoice_id': invoice.id,
281+ 'partner_id': invoice.partner_id.id,
282+ 'partner_bank_id':invoice.partner_bank_id.id,
283+ 'payment_term': invoice.payment_term.id or False,
284+ 'reference': invoice.reference,
285+ 'register_posting_date': invoice.register_posting_date,
286+ 'register_line_id': invoice.register_line_ids[0].id,
287+ 'register_id': invoice.register_line_ids[0].statement_id.id,
288+ 'state': invoice.state,
289 'type': invoice.type,
290- 'journal_type': invoice.journal_id.type,
291+ 'user_id': invoice.user_id.id,
292+ }
293+ # Create the wizard
294+ wiz_obj = self.pool.get('account.direct.invoice.wizard')
295+ wiz_id = wiz_obj.create(cr, uid, vals, context=context)
296+
297+ # recreate invoice line as temp objects
298+ wiz_invoice_line = self.pool.get('account.direct.invoice.wizard.line')
299+ for ivl in invoice.invoice_line:
300+ # copy the analytic_distribution not to modify the original one
301+ new_line_ad_id = None
302+ if ivl.analytic_distribution_id:
303+ new_line_ad_id = analytic_distribution.copy(cr, uid,
304+ ivl.analytic_distribution_id.id, context=context)
305+ ivl_values = {
306+ 'account_id':ivl.account_id.id,
307+ 'analytic_distribution_id': new_line_ad_id,
308+ 'create_date': create_date,
309+ 'invoice_wizard_id':wiz_id,
310+ 'name':ivl.name,
311+ 'original_invoice_line_id':ivl.id,
312+ 'price_unit':ivl.price_unit,
313+ 'price_subtotal':ivl.price_subtotal,
314+ 'product_id':ivl.product_id.id,
315+ 'quantity':ivl.quantity,
316+ 'reference':ivl.reference,
317+ 'uos_id':ivl.uos_id.id,
318+ }
319+ ivl_id = wiz_invoice_line.create(cr, uid, ivl_values, context=context)
320+
321+ # Update some context values
322+ context.update({
323+ 'active_id': ids[0],
324 'active_ids': ids,
325- 'from_register': True,
326- })
327+ })
328+ # Open it!
329 return {
330- 'name': name,
331- 'type': 'ir.actions.act_window',
332- 'res_model': 'account.invoice',
333- 'target': 'new',
334- 'view_mode': 'form',
335- 'view_type': 'form',
336- 'view_id': view_id,
337- 'res_id': invoice.id,
338- 'context': context,
339+ 'name': _('Direct Invoice'),
340+ 'type': 'ir.actions.act_window',
341+ 'res_model': 'account.direct.invoice.wizard',
342+ 'view_type': 'form',
343+ 'view_mode': 'form',
344+ 'target': 'new',
345+ 'res_id': [wiz_id],
346+ 'context': context,
347 }
348
349+
350 def button_duplicate(self, cr, uid, ids, context=None):
351 """
352 Copy given lines and delete all links
353@@ -2507,7 +2603,6 @@
354 for line in self.browse(cr, uid, ids, context=context):
355 if line.statement_id and line.statement_id.state != 'open':
356 raise osv.except_osv(_('Warning'), _("Register not open, you can't duplicate lines."))
357-
358 default_vals = ({
359 'name': '(copy) ' + line.name,
360 'cheque_number': None,
361
362=== added file 'register_accounting/account_direct_invoice_wizard.py'
363--- register_accounting/account_direct_invoice_wizard.py 1970-01-01 00:00:00 +0000
364+++ register_accounting/account_direct_invoice_wizard.py 2016-01-19 10:37:47 +0000
365@@ -0,0 +1,729 @@
366+#!/usr/bin/env python
367+#-*- encoding:utf-8 -*-
368+##############################################################################
369+#
370+# OpenERP, Open Source Management Solution
371+# Copyright (C) 2015 TeMPO Consulting, MSF. All Rights Reserved
372+# All Rigts Reserved
373+# Developer: Fabien MORIN
374+#
375+# This program is free software: you can redistribute it and/or modify
376+# it under the terms of the GNU Affero General Public License as
377+# published by the Free Software Foundation, either version 3 of the
378+# License, or (at your option) any later version.
379+#
380+# This program is distributed in the hope that it will be useful,
381+# but WITHOUT ANY WARRANTY; without even the implied warranty of
382+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
383+# GNU Affero General Public License for more details.
384+#
385+# You should have received a copy of the GNU Affero General Public License
386+# along with this program. If not, see <http://www.gnu.org/licenses/>.
387+#
388+##############################################################################
389+
390+import time
391+from osv import osv
392+from osv import fields
393+from tools.translate import _
394+import decimal_precision as dp
395+from register_tools import _get_date_in_period
396+from register_tools import open_register_view
397+from datetime import datetime
398+from dateutil.relativedelta import relativedelta
399+
400+
401+class account_direct_invoice_wizard(osv.osv_memory):
402+ _name = 'account.direct.invoice.wizard'
403+ _description = 'Account Invoice Temp Object'
404+
405+ def _get_type(self, cr, uid, context=None):
406+ if context is None:
407+ context = {}
408+ res = context.get('type', 'out_invoice')
409+ if context.get('search_default_supplier', False) and context.get('default_supplier', False):
410+ res = 'in_invoice'
411+ return res
412+
413+ _columns = {
414+ 'account_id': fields.many2one('account.account', 'Account',
415+ required=True,
416+ states={'draft':[('readonly', False)]},
417+ help="The partner account used for this invoice."),
418+ 'address_contact_id': fields.many2one('res.partner.address',
419+ 'Contact Address',
420+ states={'draft':[('readonly',False)]}),
421+ 'address_invoice_id': fields.many2one('res.partner.address',
422+ 'Invoice Address', required=False),
423+ 'amount_total': fields.float('Total',
424+ digits_compute=dp.get_precision('Account'), readonly=True),
425+ 'analytic_distribution_id':
426+ fields.many2one('analytic.distribution', 'Analytic Distribution',
427+ select="1"),
428+ 'check_total': fields.float('Total',
429+ digits_compute=dp.get_precision('Account'),
430+ states={'open':[('readonly', True)],
431+ 'close':[('readonly', True)],
432+ 'paid':[('readonly', True)]}),
433+ 'comment': fields.text('Additional Information'),
434+ 'company_id': fields.many2one('res.company', 'Company',
435+ required=True, change_default=True,
436+ states={'draft':[('readonly',False)]}),
437+ 'create_date': fields.datetime('Created', readonly=True),
438+ 'currency_id': fields.many2one('res.currency', string="Currency"),
439+ 'date_invoice': fields.date('Posting Date', select=True),
440+ 'document_date': fields.date('Document date'),
441+ 'invoice_wizard_line': fields.one2many('account.direct.invoice.wizard.line',
442+ 'invoice_wizard_id', 'Invoice Wizard Lines',
443+ states={'draft':[('readonly',False)]}),
444+ 'is_direct_invoice': fields.boolean("Is direct invoice?",
445+ readonly=True, default=True),
446+ 'journal_id': fields.many2one('account.journal', 'Journal',
447+ required=True, readonly=True),
448+ 'name': fields.char('Description', size=64, select=True,
449+ readonly=True, states={'draft':[('readonly',False)]}),
450+ 'number': fields.related('move_id','name', type='char',
451+ readonly=True, size=64, relation='account.move', store=True,
452+ string='Number'),
453+ 'origin': fields.char('Source Document', size=512,
454+ help="Referencie of the document that produced this invoice.",
455+ readonly=True, states={'draft':[('readonly',False)]}),
456+ 'original_invoice_id': fields.many2one('account.invoice', 'Original Invoice'),
457+ 'partner_id': fields.many2one('res.partner', 'Partner',
458+ change_default=True, required=True),
459+ 'partner_bank_id': fields.many2one('res.partner.bank',
460+ 'Bank Account',
461+ help='Bank Account Number, Company bank account if '
462+ 'Invoice is customer or supplier refund, otherwise '
463+ 'Partner bank account number.', readonly=True,
464+ states={'draft':[('readonly',False)]}),
465+ 'payment_term': fields.many2one('account.payment.term',
466+ 'Payment Term', states={'draft':[('readonly',False)]},
467+ help="If you use payment terms, the due date will be computed "
468+ "automatically at the generation of accounting entries. If you"
469+ " keep the payment term and the due date empty, it means "
470+ "direct payment. The payment term may compute several due "
471+ "dates, for example 50% now, 50% in one month."),
472+ 'reference': fields.char('Invoice Reference', size=64,
473+ help="The partner reference of this invoice."),
474+ 'register_posting_date': fields.date(string=\
475+ "Register posting date for Direct Invoice", required=False),
476+ 'register_id': fields.many2one('account.bank.statement', 'Register', readonly=True),
477+ 'register_line_id': fields.many2one('account.bank.statement.line',
478+ 'Register Line', readonly=True),
479+ 'user_id': fields.many2one('res.users', 'Salesman'),
480+ 'state': fields.selection([
481+ ('draft','Draft'),
482+ ('proforma','Pro-forma'),
483+ ('proforma2','Pro-forma'),
484+ ('open','Open'),
485+ ('paid','Paid'),
486+ ('cancel','Cancelled')
487+ ],'State', select=True, readonly=True,),
488+ 'type': fields.selection([
489+ ('out_invoice','Customer Invoice'),
490+ ('in_invoice','Supplier Invoice'),
491+ ('out_refund','Customer Refund'),
492+ ('in_refund','Supplier Refund'),
493+ ],'Type', readonly=True, select=True, change_default=True),
494+ }
495+
496+ _defaults = {
497+ 'register_id': False,
498+ 'payment_term': False,
499+ 'type': _get_type,
500+ 'state': 'draft',
501+ 'check_total': 0.0,
502+ 'user_id': lambda s, cr, u, c: u,
503+ }
504+
505+ def vacuum(self, cr, uid):
506+ one_hour = (datetime.now() + relativedelta(hours=-1)).strftime("%Y-%m-%d %H:%M:%S")
507+ unlink_ids = self.search(cr, uid, [('create_date', '<', one_hour)])
508+ if unlink_ids:
509+ return self.unlink(cr, uid, unlink_ids)
510+ return True
511+
512+ def unlink(self, cr, uid, ids, context=None):
513+ """ delete the associated analytic_distribution
514+ """
515+ analytic_distribution = self.pool.get('analytic.distribution')
516+ for obj in self.browse(cr, uid, ids, context):
517+ # delete analytic_distribution linked to this wizard
518+ if obj.analytic_distribution_id:
519+ analytic_distribution.unlink(cr, uid,
520+ obj.analytic_distribution_id.id)
521+ return super(account_direct_invoice_wizard, self).unlink(cr, uid, ids)
522+
523+ def compute_wizard(self, cr, uid, ids, context=None):
524+ """
525+ Check invoice lines and compute the total invoice amount
526+ """
527+ for wiz_inv in self.browse(cr, uid, ids):
528+ amount = 0
529+ for line in wiz_inv.invoice_wizard_line:
530+ amount += line.price_subtotal
531+ self.write(cr, uid, [wiz_inv.id], {'amount_total': amount})
532+ return True
533+
534+ def check_analytic_distribution(self, cr, uid, ids):
535+ """
536+ Check that all line have a valid analytic distribution state
537+ """
538+ if isinstance(ids, (int, long)):
539+ ids = [ids]
540+ for w in self.browse(cr, uid, ids):
541+ for l in w.invoice_wizard_line:
542+ if l.analytic_distribution_state != 'valid':
543+ raise osv.except_osv(_('Warning'),
544+ _('Analytic distribution is not valid for this line: %s') % (l.name or '',))
545+ return True
546+
547+ def invoice_create_wizard(self, cr, uid, ids, context=None):
548+ """
549+ Take information from wizard in order to create an invoice, invoice lines and to post a register line that permit to reconcile the invoice.
550+ """
551+ self.check_analytic_distribution(cr, uid, ids)
552+
553+ # Prepare some value
554+ absl_obj = self.pool.get('account.bank.statement.line')
555+ inv_obj = self.pool.get('account.invoice')
556+ invl_obj = self.pool.get('account.invoice.line')
557+ wiz_obj = self
558+ wiz_line_obj = self.pool.get('account.direct.invoice.wizard.line')
559+ analytic_distribution = self.pool.get('analytic.distribution')
560+
561+ # Get the original invoice
562+ inv_id = wiz_obj.browse(cr, uid, ids, context)[0].original_invoice_id.id
563+ invoice = inv_obj.browse(cr, uid, inv_id)
564+
565+ if invoice and invoice.state != 'draft':
566+ raise osv.except_osv(_('Error'), _('The invoice cannot be modified'
567+ ' as it is in %s state (should be draft).' % (invoice.state)))
568+
569+ vals = {}
570+ inv = self.read(cr, uid, ids[0], [])
571+ for val in inv:
572+ if val in ('id', 'wiz_invoice_line', 'register_id'):
573+ continue
574+ if isinstance(inv[val], tuple):
575+ vals[val] = inv[val][0]
576+ elif isinstance(inv[val], list):
577+ continue
578+ elif inv[val]:
579+ vals[val] = inv[val]
580+ vals['invoice_line'] = []
581+ amount = 0
582+ if inv['invoice_wizard_line']:
583+ for line in wiz_line_obj.browse(cr, uid,
584+ inv['invoice_wizard_line']):
585+ # line level reference overrides header level reference
586+ line_reference = False
587+ if line.reference:
588+ line_reference = line.reference
589+ elif inv['reference']:
590+ line_reference = inv['reference']
591+ vals['invoice_line'].append((line.original_invoice_line_id.id,
592+ {
593+ 'product_id': line.product_id.id,
594+ 'account_id': line.account_id.id,
595+ 'account_analytic_id': line.account_analytic_id.id,
596+ 'analytic_distribution_id': line.analytic_distribution_id.id,
597+ 'quantity': line.quantity,
598+ 'invoice_id': vals['original_invoice_id'],
599+ 'price_unit': line.price_unit,
600+ 'name': line.name,
601+ 'uos_id': line.uos_id.id,
602+ 'reference': line_reference,
603+ })
604+ )
605+ amount += line.price_subtotal
606+
607+ # Retrieve period
608+ register = self.pool.get('account.bank.statement').browse(cr, uid, [inv['register_id']], context=context)[0]
609+ period = register and register.period_id and register.period_id.id or False
610+ # Check the dates
611+ if vals['date_invoice'] and vals['register_posting_date']:
612+ if vals['date_invoice'] < register.period_id.date_start or \
613+ vals['date_invoice'] > register.period_id.date_stop:
614+ raise osv.except_osv(_('Warning'), _('Direct Invoice posting date is outside of the register period!'))
615+ elif vals['register_posting_date'] < register.period_id.date_start or \
616+ vals['register_posting_date'] > register.period_id.date_stop:
617+ raise osv.except_osv(_('Warning'), _('Register Line posting date is outside of the register period!'))
618+ elif vals['date_invoice'] > vals['register_posting_date']:
619+ raise osv.except_osv(_('Warning'), _('Direct Invoice posting date must be sooner or equal to the register line posting date!'))
620+ vals.update({'date_invoice': vals['date_invoice']})
621+ vals.update({'register_posting_date': vals['register_posting_date']})
622+
623+ # delete original analytic_distribution because a copie has been done
624+ # and linked to the original invoice
625+ if invoice.analytic_distribution_id:
626+ analytic_distribution.unlink(cr, uid, invoice.analytic_distribution_id.id)
627+
628+ # update the invoice
629+ vals_copy = vals.copy()
630+ # invoice lines are processed just after
631+ vals_copy.pop('invoice_line')
632+ inv_obj.write(cr, uid, [inv_id], vals_copy, context)
633+
634+ # get line id list
635+ invl_id_list = [x.id for x in wiz_obj.browse(cr, uid, ids,
636+ context)[0].original_invoice_id.invoice_line]
637+
638+ # update the invoice lines
639+ not_deleted_id_list = []
640+ for original_line_id, vals_dict in vals['invoice_line']:
641+ # get the original invoice line
642+ if original_line_id: # if there is a corresponding original invoice line
643+ # delete original analytic_distribution because a copie has been done
644+ # and will be linked to the original invoice_line
645+ orig_line = invl_obj.browse(cr, uid, original_line_id, context)
646+ if orig_line.analytic_distribution_id:
647+ analytic_distribution.unlink(cr, uid,
648+ orig_line.analytic_distribution_id.id)
649+ not_deleted_id_list.append(original_line_id)
650+ invl_obj.write(cr, uid, [original_line_id], vals_dict, context)
651+ else:
652+ # this is a new line, create is called
653+ invl_obj.create(cr, uid, vals_dict, context)
654+
655+ # if lines exist in the invoice but not in the wizard, that means that
656+ # they have been deleted in the wizard. So we need to delete them in the
657+ # invoice
658+ for original_line_id in tuple(set(invl_id_list) - set(not_deleted_id_list)):
659+ invl_obj.unlink(cr, uid, original_line_id, context=context)
660+
661+ # update the invoice check_total with all line and
662+ # link invoice and register_line
663+ amount = 0.0
664+ for l in invoice.invoice_line:
665+ amount += l.price_subtotal
666+ inv_obj.write(cr, uid, [inv_id],
667+ {'check_total': amount,
668+ 'is_direct_invoice': True,
669+ 'register_line_ids': [(4, vals['register_line_id'])]},
670+ context)
671+ absl_obj.write(cr, uid, [x.id for x in invoice.register_line_ids], {'amount': -1 * amount}, context)
672+
673+ # Update the attached register line and link the invoice to the register
674+ reg_line_id = vals['register_line_id']
675+ values = {
676+ 'account_id': vals['account_id'],
677+ 'currency_id': vals['currency_id'],
678+ 'date': _get_date_in_period(self, cr, uid,
679+ vals['register_posting_date'] or\
680+ time.strftime('%Y-%m-%d'), period, context=context),
681+ 'document_date': vals['document_date'],
682+ 'direct_invoice': True,
683+ 'amount_out': amount,
684+ 'invoice_id': inv_id,
685+ 'partner_type': 'res.partner,%d'%(vals['partner_id'], ),
686+ 'statement_id': inv['register_id'],
687+ 'name': 'Direct Invoice',
688+ 'ref': inv['reference'], # register line always takes header reference
689+ }
690+ absl_obj.write(cr, uid, [reg_line_id], values)
691+
692+ # set analytic_ditribution to None on the wizard line not to
693+ # delete it when the wizard will be deleted
694+ wiz_line_obj.write(cr, uid, inv['invoice_wizard_line'],
695+ {'analytic_distribution_id': None})
696+ # Delete the wizard lines:
697+ wiz_line_obj.unlink(cr, uid, inv['invoice_wizard_line'], context=context)
698+
699+ # set analytic_ditribution to None on the wizard not to
700+ # delete it when the wizard will be deleted
701+ wiz_obj.write(cr, uid, ids, {'analytic_distribution_id': None})
702+
703+ # Delete the wizard
704+ self.unlink(cr, uid, ids, context=context)
705+
706+ # update invoice
707+ inv_obj._direct_invoice_updated(cr, uid, [inv_id], context)
708+
709+ view = open_register_view(self, cr, uid, inv['register_id'])
710+ # UF-2308: When closing the Direct Invoice, just refresh only the register lines, and no the whole view
711+ # to avoid having everything reset to default state.
712+ view['o2m_refresh'] = 'line_ids'
713+ view['type'] = 'ir.actions.act_window_close'
714+ return view
715+
716+ def invoice_reset_wizard(self, cr, uid, ids, context=None):
717+ """
718+ Reset the invoice by reseting some fields
719+ """
720+ self.write(cr, uid, ids,{
721+ 'invoice_wizard_line': [(5,)],
722+ 'register_posting_date': time.strftime('%Y-%m-%d'),
723+ 'date_invoice': time.strftime('%Y-%m-%d'),
724+ 'partner_id': False,
725+ 'address_invoice_id': False,
726+ 'account_id': False,
727+ 'state': 'draft',
728+ 'analytic_distribution_id': False,
729+ 'document_date': time.strftime('%Y-%m-%d'),
730+ })
731+ return True
732+
733+ def button_analytic_distribution(self, cr, uid, ids, context=None):
734+ """
735+ Launch analytic distribution wizard on an invoice
736+ """
737+ # Some verifications
738+ if not context:
739+ context = {}
740+ if isinstance(ids, (int, long)):
741+ ids = [ids]
742+ # Prepare some values
743+ invoice = self.browse(cr, uid, ids[0], context=context)
744+ amount = 0.0
745+ # Search elements for currency
746+ company_currency = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id
747+ currency = invoice.currency_id and invoice.currency_id.id or company_currency
748+ for line in invoice.invoice_wizard_line:
749+ amount += line.price_subtotal
750+ # Get analytic_distribution_id
751+ distrib_id = invoice.analytic_distribution_id and invoice.analytic_distribution_id.id
752+ account_id = invoice.account_id and invoice.account_id.id
753+ # Prepare values for wizard
754+ vals = {
755+ 'total_amount': amount,
756+ 'direct_invoice_id': invoice.id,
757+ 'currency_id': currency or False,
758+ 'state': 'dispatch',
759+ 'account_id': account_id or False,
760+ }
761+ if distrib_id:
762+ vals.update({'distribution_id': distrib_id,})
763+ # Create the wizard
764+ wiz_obj = self.pool.get('analytic.distribution.wizard')
765+ wiz_id = wiz_obj.create(cr, uid, vals, context=context)
766+ # Update some context values
767+ context.update({
768+ 'active_id': ids[0],
769+ 'active_ids': ids,
770+ })
771+ # Open it!
772+ return {
773+ 'name': _('Global analytic distribution'),
774+ 'type': 'ir.actions.act_window',
775+ 'res_model': 'analytic.distribution.wizard',
776+ 'view_type': 'form',
777+ 'view_mode': 'form',
778+ 'target': 'new',
779+ 'res_id': [wiz_id],
780+ 'context': context,
781+ }
782+
783+ def button_reset_distribution(self, cr, uid, ids, context=None):
784+ """
785+ Reset analytic distribution on all account direct invoice wizard lines.
786+ To do this, just delete the analytic_distribution id link on each invoice line.
787+ """
788+ if context is None:
789+ context = {}
790+ if isinstance(ids, (int, long)):
791+ ids = [ids]
792+ # Prepare some values
793+ invl_obj = self.pool.get(self._name + '.line')
794+ # Search invoice lines
795+ to_reset = invl_obj.search(cr, uid, [('invoice_wizard_id', 'in', ids)])
796+ invl_obj.write(cr, uid, to_reset, {'analytic_distribution_id': False})
797+ return True
798+
799+ def onchange_partner_id(self, cr, uid, ids, ctype, partner_id,\
800+ date_invoice=False, payment_term=False,
801+ partner_bank_id=False, company_id=False,
802+ is_inkind_donation=False, is_intermission=False,
803+ is_debit_note=False, is_direct_invoice=False):
804+ # just call the original method from account.invoice
805+ return self.pool.get('account.invoice').onchange_partner_id(cr, uid, [],
806+ ctype, partner_id, date_invoice, payment_term, partner_bank_id,
807+ company_id, is_inkind_donation, is_intermission, is_debit_note,
808+ True)
809+
810+account_direct_invoice_wizard()
811+
812+
813+class account_direct_invoice_wizard_line(osv.osv_memory):
814+ _name = 'account.direct.invoice.wizard.line'
815+ _description = 'Account Invoice Line Temp Object'
816+
817+ def _get_distribution_state(self, cr, uid, ids, name, args, context=None):
818+ """
819+ Get state of distribution:
820+ - if compatible with the invoice line, then "valid"
821+ - if no distribution, take a tour of invoice distribution, if compatible, then "valid"
822+ - if no distribution on invoice line and invoice, then "none"
823+ - all other case are "invalid"
824+ """
825+ # Some verifications
826+ if not context:
827+ context = {}
828+ if isinstance(ids, (int, long)):
829+ ids = [ids]
830+ # Prepare some values
831+ res = {}
832+ # Browse all given lines
833+ for line in self.browse(cr, uid, ids, context=context):
834+ if line.from_yml_test:
835+ res[line.id] = 'valid'
836+ else:
837+ # UF-2115: test for elements
838+ line_distribution_id = False
839+ invoice_distribution_id = False
840+ line_account_id = False
841+ if line.analytic_distribution_id:
842+ line_distribution_id = line.analytic_distribution_id.id
843+ if line.invoice_wizard_id and line.invoice_wizard_id.analytic_distribution_id:
844+ invoice_distribution_id = line.invoice_wizard_id.analytic_distribution_id.id
845+ if line.account_id:
846+ line_account_id = line.account_id.id
847+ res[line.id] = self.pool.get('analytic.distribution')._get_distribution_state(cr, uid, line_distribution_id, invoice_distribution_id, line_account_id)
848+ return res
849+
850+ def _get_distribution_state_recap(self, cr, uid, ids, name, arg, context=None):
851+ """
852+ Get a recap from analytic distribution state and if it come from header or not.
853+ """
854+ if isinstance(ids, (int, long)):
855+ ids = [ids]
856+ res = {}
857+ for invl in self.browse(cr, uid, ids):
858+ res[invl.id] = ''
859+ if not invl.is_allocatable:
860+ continue
861+ from_header = ''
862+ if invl.have_analytic_distribution_from_header:
863+ from_header = _(' (from header)')
864+ res[invl.id] = '%s%s' % (self.pool.get('ir.model.fields').get_browse_selection(cr, uid, invl, 'analytic_distribution_state', context), from_header)
865+ return res
866+
867+ def _have_analytic_distribution_from_header(self, cr, uid, ids, name, arg, context=None):
868+ """
869+ If invoice have an analytic distribution, return False, else return True
870+ """
871+ # Some verifications
872+ if not context:
873+ context = {}
874+ if isinstance(ids, (int, long)):
875+ ids = [ids]
876+ res = {}
877+ for inv in self.browse(cr, uid, ids, context=context):
878+ res[inv.id] = True
879+ if inv.analytic_distribution_id:
880+ res[inv.id] = False
881+ return res
882+
883+ def _get_is_allocatable(self, cr, uid, ids, name, arg, context=None):
884+ """
885+ If analytic-a-holic account, then this account is allocatable.
886+ """
887+ if isinstance(ids, (int, long)):
888+ ids = [ids]
889+ res = {}
890+ for invl in self.browse(cr, uid, ids):
891+ res[invl.id] = True
892+ if invl.account_id and not invl.account_id.is_analytic_addicted:
893+ res[invl.id] = False
894+ return res
895+
896+ def _get_inactive_product(self, cr, uid, ids, field_name, args, context=None):
897+ '''
898+ Fill the error message if the product of the line is inactive
899+ '''
900+ res = {}
901+ for line in self.browse(cr, uid, ids, context=context):
902+ res[line.id] = {'inactive_product': False,
903+ 'inactive_error': ''}
904+ if line.invoice_wizard_id and line.invoice_wizard_id.state not in ('cancel', 'done') and line.product_id and not line.product_id.active:
905+ res[line.id] = {
906+ 'inactive_product': True,
907+ 'inactive_error': _('The product in line is inactive !')
908+ }
909+ return res
910+
911+ def _amount_line(self, cr, uid, ids, prop, unknow_none, unknow_dict):
912+ res = {}
913+ tax_obj = self.pool.get('account.tax')
914+ cur_obj = self.pool.get('res.currency')
915+ for line in self.browse(cr, uid, ids):
916+ price = line.price_unit * (1-(line.discount or 0.0)/100.0)
917+ taxes = tax_obj.compute_all(cr, uid, line.invoice_line_tax_id,
918+ price, line.quantity, product=line.product_id,
919+ address_id=line.invoice_wizard_id.address_invoice_id,
920+ partner=line.invoice_wizard_id.partner_id)
921+ res[line.id] = taxes['total']
922+ if line.invoice_wizard_id:
923+ cur = line.invoice_wizard_id.currency_id
924+ res[line.id] = cur_obj.round(cr, uid, cur.rounding, res[line.id])
925+ return res
926+
927+ _columns = {
928+ 'move_id': fields.many2one('account.move', 'Journal Entry',
929+ readonly=True, select=1, ondelete='restrict',
930+ help="Link to the automatically generated Journal Items."),
931+ 'analytic_distribution_id': fields.many2one('analytic.distribution',
932+ 'Analytic Distribution', select="1"),
933+ 'analytic_distribution_state': fields.function(_get_distribution_state,
934+ method=True, type='selection',
935+ selection=[('none', 'None'),
936+ ('valid', 'Valid'),
937+ ('invalid', 'Invalid')],
938+ string="Distribution state",
939+ help="Informs from distribution state among 'none',"
940+ " 'valid', 'invalid."),
941+ 'analytic_distribution_state_recap': fields.function(_get_distribution_state_recap,
942+ method=True, type='char', size=30,
943+ string="Distribution",
944+ help="Informs you about analaytic distribution state among 'none',"
945+ " 'valid', 'invalid', from header or not, or no analytic distribution"),
946+ 'create_date': fields.datetime('Created', readonly=True),
947+ 'from_yml_test': fields.boolean('Only used to pass addons unit test',
948+ readonly=True, help='Never set this field to true !'),
949+ 'have_analytic_distribution_from_header': fields.function(_have_analytic_distribution_from_header,
950+ method=True, type='boolean', string='Header Distrib.?'),
951+ 'inactive_product': fields.function(_get_inactive_product, method=True,
952+ type='boolean', string='Product is inactive', store=False,
953+ multi='inactive'),
954+ 'invoice_wizard_id': fields.many2one('account.direct.invoice.wizard',
955+ 'Invoice Wizard',
956+ ondelete='cascade', select=True),
957+ 'is_allocatable': fields.function(_get_is_allocatable, method=True,
958+ type='boolean', string="Is allocatable?", readonly=True, store=False),
959+ 'reference': fields.char(string="Reference", size=64),
960+ 'name': fields.char('Description', size=256, required=True),
961+ 'origin': fields.char('Origin', size=512,
962+ help="Reference of the document that produced this invoice."),
963+ 'uos_id': fields.many2one('product.uom', 'Unit of Measure',
964+ ondelete='set null'),
965+ 'original_invoice_line_id': fields.many2one('account.invoice.line',
966+ 'Original Invoice Line'),
967+ 'product_id': fields.many2one('product.product', 'Product',
968+ ondelete='set null'),
969+ 'account_id': fields.many2one('account.account', 'Account',
970+ required=True, domain=[('type','<>','view'), ('type', '<>',
971+ 'closed')],
972+ help="The income or expense account related to the selected product."),
973+ 'price_unit': fields.float('Unit Price', required=True,
974+ digits_compute=dp.get_precision('Account')),
975+ 'price_subtotal': fields.function(_amount_line, method=True,
976+ string='Subtotal', type="float",
977+ digits_compute=dp.get_precision('Account'), store=False),
978+ 'quantity': fields.float('Quantity', required=True),
979+ 'discount': fields.float('Discount (%)',
980+ digits_compute=dp.get_precision('Account')),
981+ 'invoice_line_tax_id': fields.many2many('account.tax',
982+ 'account_invoice_line_tax', 'invoice_line_id', 'tax_id', 'Taxes',
983+ domain=[('parent_id','=',False)]),
984+ 'note': fields.text('Notes'),
985+ 'account_analytic_id': fields.many2one('account.analytic.account',
986+ 'Analytic Account'),
987+ 'company_id': fields.related('invoice_wizard_id','company_id',type='many2one',
988+ relation='res.company', string='Company', store=True, readonly=True),
989+ 'partner_id': fields.related('invoice_wizard_id','partner_id',type='many2one',
990+ relation='res.partner', string='Partner',store=True),
991+ 'inactive_error': fields.function(_get_inactive_product, method=True,
992+ type='char', string='Comment', store=False, multi='inactive'),
993+ 'newline': fields.boolean('New line'),
994+ }
995+
996+ _defaults = {
997+ 'quantity': lambda *x: 1,
998+ 'price_unit': lambda *x: 0,
999+ 'newline': lambda *a: True,
1000+ 'have_analytic_distribution_from_header': lambda *a: True,
1001+ 'is_allocatable': lambda *a: True,
1002+ 'analytic_distribution_state': lambda *a: '',
1003+ 'analytic_distribution_state_recap': lambda *a: '',
1004+ 'inactive_product': False,
1005+ 'inactive_error': lambda *a: '',
1006+ 'original_invoice_line_id': False,
1007+ }
1008+
1009+ def unlink(self, cr, uid, ids, context=None):
1010+ """ delete the associated analytic_distribution
1011+ """
1012+ analytic_distribution = self.pool.get('analytic.distribution')
1013+ for obj in self.browse(cr, uid, ids, context):
1014+ # delete analytic_distribution linked to this wizard
1015+ if obj.analytic_distribution_id:
1016+ analytic_distribution.unlink(cr, uid,
1017+ obj.analytic_distribution_id.id)
1018+ return super(account_direct_invoice_wizard_line, self).unlink(cr, uid, ids)
1019+
1020+ def onchange_account_id(self, cr, uid, ids, fposition_id, account_id):
1021+ # just call the original method from account.invoice.line
1022+ return self.pool.get('account.invoice.line').onchange_account_id(cr, uid, ids,
1023+ fposition_id, account_id)
1024+
1025+ def button_analytic_distribution(self, cr, uid, ids, context=None):
1026+ """
1027+ Launch analytic distribution wizard on an invoice line
1028+ """
1029+ # Some verifications
1030+ if not context:
1031+ context = {}
1032+ if isinstance(ids, (int, long)):
1033+ ids = [ids]
1034+ if not ids:
1035+ raise osv.except_osv(_('Error'), _('No invoice line given. '
1036+ 'Please save your invoice line before.'))
1037+ # Prepare some values
1038+ invoice_line = self.browse(cr, uid, ids[0], context=context)
1039+ negative_inv = False
1040+ amount = invoice_line.price_subtotal or 0.0
1041+ # Search elements for currency
1042+ company_currency = self.pool.get('res.users').browse(cr, uid, uid,
1043+ context=context).company_id.currency_id.id
1044+ currency = invoice_line.invoice_wizard_id.currency_id and \
1045+ invoice_line.invoice_wizard_id.currency_id.id or company_currency
1046+ # Change amount sign if necessary
1047+ if invoice_line.invoice_wizard_id.type in ['out_invoice', 'in_refund']:
1048+ negative_inv = True
1049+ if negative_inv:
1050+ amount = -1 * amount
1051+ # Get analytic distribution id from this line
1052+ distrib_id = invoice_line and invoice_line.analytic_distribution_id and\
1053+ invoice_line.analytic_distribution_id.id or False
1054+ # Prepare values for wizard
1055+ vals = {
1056+ 'total_amount': amount,
1057+ 'account_direct_invoice_wizard_line_id': invoice_line.id,
1058+ 'currency_id': currency or False,
1059+ 'state': 'dispatch',
1060+ 'account_id': invoice_line.account_id and invoice_line.account_id.id or False,
1061+ 'posting_date': invoice_line.invoice_wizard_id.date_invoice,
1062+ 'document_date': invoice_line.invoice_wizard_id.document_date,
1063+ }
1064+ if distrib_id:
1065+ vals.update({'distribution_id': distrib_id,})
1066+ # Create the wizard
1067+ wiz_obj = self.pool.get('analytic.distribution.wizard')
1068+ wiz_id = wiz_obj.create(cr, uid, vals, context=context)
1069+ # Update some context values
1070+ context.update({
1071+ 'active_id': ids[0],
1072+ 'active_ids': ids,
1073+ })
1074+ # Open it!
1075+ return {
1076+ 'name': _('Analytic distribution'),
1077+ 'type': 'ir.actions.act_window',
1078+ 'res_model': 'analytic.distribution.wizard',
1079+ 'view_type': 'form',
1080+ 'view_mode': 'form',
1081+ 'target': 'new',
1082+ 'res_id': [wiz_id],
1083+ 'context': context,
1084+ }
1085+
1086+ def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, address_invoice_id=False, currency_id=False, context=None):
1087+ # just call the original method from account.invoice.line
1088+ return self.pool.get('account.invoice.line').product_id_change(cr, uid, ids,
1089+ product, uom, qty=qty, name=name, type=type, partner_id=partner_id,
1090+ fposition_id=fposition_id, price_unit=price_unit,
1091+ address_invoice_id=address_invoice_id, currency_id=currency_id,
1092+ context=context)
1093+
1094+account_direct_invoice_wizard_line()
1095
1096=== modified file 'register_accounting/account_invoice_view.xml'
1097--- register_accounting/account_invoice_view.xml 2015-08-21 07:16:30 +0000
1098+++ register_accounting/account_invoice_view.xml 2016-01-19 10:37:47 +0000
1099@@ -141,7 +141,7 @@
1100 <field name="inherit_id" eval="False" />
1101 <field name="priority">250</field>
1102 <field name="arch" type="xml">
1103- <form string="Supplier Invoice" hide_save_button="1" hide_duplicate_button="1" hide_new_button="1">
1104+ <form string="Supplier Invoice" hide_save_button="1" hide_duplicate_button="1" hide_new_button="1" hide_edit_button="1" hide_delete_button="1" noteditable="1">
1105 <group colspan="4" col="8">
1106 <field name="is_direct_invoice" invisible="1" readonly="1"/>
1107 <field name="journal_id" on_change="onchange_journal_id(journal_id)" widget="selection" attrs="{'readonly': [('is_direct_invoice', '=', True)]}" />
1108@@ -185,9 +185,6 @@
1109 <field name="name"/>
1110 <field name="reference" />
1111 <field domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('restricted_area', '=', 'invoice_lines')]" name="account_id" on_change="onchange_account_id(parent.fiscal_position,account_id)"/>
1112- <button name="button_analytic_distribution" string="Analytical Distribution" type="object" icon="terp-stock_symbol-selection"
1113- context="{'d_journal_id': parent.journal_id , 'd_partner_id': parent.partner_id, 'd_address_invoice_id':parent.address_invoice_id,'d_date_invoice': parent.date_invoice, 'd_register_posting_date': parent.register_posting_date, 'd_document_date': parent.document_date, 'd_account_id': parent.account_id, 'd_partner_bank_id': parent.partner_bank_id, 'd_payment_term': parent.payment_term, 'd_name': parent.name, 'd_origine': parent.origin, 'd_address_contact_id': parent.address_contact_id, 'd_user_id': parent.user_id, 'd_comment': parent.comment, 'd_reference': parent.reference}"
1114- />
1115 <field name="analytic_distribution_state_recap" attrs="{'invisible': [('is_allocatable', '=', False)]}"/>
1116 <field name="analytic_distribution_state" invisible="1"/>
1117 <field name="have_analytic_distribution_from_header" invisible="1"/>
1118@@ -249,7 +246,7 @@
1119 <field name="type">tree</field>
1120 <field name="priority">250</field>
1121 <field name="arch" type="xml">
1122- <tree colors="blue:state in ('draft');black:state in ('proforma','proforma2','open');gray:state in ('cancel')" string="Invoice" hide_new_button="1" >
1123+ <tree noteditable="1" colors="blue:state in ('draft');black:state in ('proforma','proforma2','open');gray:state in ('cancel')" string="Invoice" hide_new_button="1" hide_delete_button="1">
1124 <field name="document_date"/>
1125 <field name="date_invoice"/>
1126 <field name="number"/>
1127
1128=== added file 'register_accounting/wizard/account_direct_invoice_wizard_view.xml'
1129--- register_accounting/wizard/account_direct_invoice_wizard_view.xml 1970-01-01 00:00:00 +0000
1130+++ register_accounting/wizard/account_direct_invoice_wizard_view.xml 2016-01-19 10:37:47 +0000
1131@@ -0,0 +1,112 @@
1132+<?xml version="1.0" encoding="utf-8"?>
1133+<openerp>
1134+ <data>
1135+
1136+ <!-- Direct Supplier Invoice wizard -->
1137+ <record model="ir.ui.view" id="direct_supplier_invoice_wizard_lines_tree">
1138+ <field name="name">direct.supplier.invoice.wizard.lines.tree</field>
1139+ <field name="model">account.direct.invoice.wizard.lines</field>
1140+ <field name="type">tree</field>
1141+ <field name="arch" type='xml'>
1142+ <!-- A changer XXX-->
1143+ <tree string="" editable="top">
1144+ <field name="is_percentage_amount_touched" invisible="1" />
1145+ <field name="destination_id" context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('posting_date')}"/>
1146+ <field name="analytic_id" domain="[('type', '!=', 'view'), ('category', '=', 'OC'), ('state', '=', 'open')]" string="Cost Center" context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('posting_date')}"/>
1147+ <field name="percentage" sum="Total Percentage" digits="(16,2)"/>
1148+ <field name="amount" sum="Total Amount"/>
1149+ </tree>
1150+ </field>
1151+ </record>
1152+
1153+ <record id="direct_supplier_invoice_wizard_view" model="ir.ui.view">
1154+ <field name="name">direct.supplier.invoice.wizard.view</field>
1155+ <field name="model">account.direct.invoice.wizard</field>
1156+ <field name="type">form</field>
1157+ <field name="arch" type="xml">
1158+ <form string="Supplier Invoice" noteditable="state=='paid'">
1159+ <group colspan="4" col="6">
1160+ <field name="is_direct_invoice" invisible="1" readonly="1"/>
1161+ <field name="journal_id" domain="[('is_current_instance','=',True),('type','=','purchase')]" on_change="onchange_journal_id(journal_id)"/>
1162+ <field name="number" readonly="1"/>
1163+ <field name="type" invisible="1"/>
1164+ <field name="currency_id" readonly="1" />
1165+ <newline/>
1166+ <field name="partner_id" on_change="onchange_partner_id(type,partner_id,date_invoice,payment_term, partner_bank_id,company_id,is_inkind_donation,is_intermission,is_debit_note,'1')" context="{'default_customer': 0, 'search_default_supplier': 1, 'default_supplier': 1}" required="1"/>
1167+ <field domain="[('partner_id','=',partner_id)]" name="address_invoice_id" required="1"/>
1168+ <newline/>
1169+ <field name="document_date" required="1"/>
1170+ <field name="date_invoice" required="1" readonly="1"/>
1171+ <field name="register_posting_date" required="1" readonly="1"/>
1172+ <field name="reference" />
1173+ <field name="state" invisible="1"/>
1174+ <newline/>
1175+ <label string="" colspan="4"/>
1176+ <field name="amount_total"/>
1177+ </group>
1178+ <group colspan="8" col="8" attrs="{'invisible': [('analytic_distribution_id', '=', False)]}">
1179+ <button name="button_analytic_distribution" string="Analytical Distribution" type="object" icon="terp-check" context="context" colspan="4" attrs="{'invisible': [('analytic_distribution_id', '=', False)]}"/>
1180+ <button name="button_reset_distribution" string="Reset AD at line level" type="object" icon="gtk-undelete" colspan="4" states="draft"/>
1181+ </group>
1182+ <group colspan="8" col="8" attrs="{'invisible': [('analytic_distribution_id', '!=', False)]}">
1183+ <button name="button_analytic_distribution" string="Analytical Distribution" type="object" icon="terp-emblem-important" context="context" colspan="4" attrs="{'invisible': [('analytic_distribution_id', '!=', False)]}"/>
1184+ <button name="button_reset_distribution" string="Reset AD at line level" type="object" icon="gtk-undelete" colspan="4" states="draft"/>
1185+ </group>
1186+ <field name="analytic_distribution_id" invisible="1"/>
1187+ <group colspan="2"/>
1188+ <notebook colspan="4">
1189+ <page string="Invoice">
1190+ <field name="account_id" domain="[('company_id', '=', company_id), ('restricted_area', '=', 'in_invoice')]" required="1"/>
1191+ <field name="check_total" readonly="1" invisible="1"/>
1192+ <field colspan="4" default_get="{'check_total': check_total, 'address_invoice_id':
1193+ address_invoice_id, 'partner_id': partner_id, 'price_type': 'price_type' in dir() and price_type or False}"
1194+ name="invoice_wizard_line" nolabel="1">
1195+ <tree editable="bottom" string="Invoice lines" colors="red:analytic_distribution_state == 'invalid' or inactive_product == True;black:inactive_product == False and analytic_distribution_state in ('none','valid')">
1196+ <field name="name"/>
1197+ <field name="reference" />
1198+ <field domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('restricted_area', '=', 'invoice_lines')]" name="account_id" on_change="onchange_account_id(parent.fiscal_position,account_id)"/>
1199+ <button name="button_analytic_distribution" string="Analytical Distribution" type="object" icon="terp-stock_symbol-selection"
1200+ context="{'d_journal_id': parent.journal_id , 'd_partner_id': parent.partner_id, 'd_address_invoice_id':parent.address_invoice_id,'d_date_invoice': parent.date_invoice, 'd_register_posting_date': parent.register_posting_date, 'd_document_date': parent.document_date, 'd_account_id': parent.account_id, 'd_partner_bank_id': parent.partner_bank_id, 'd_payment_term': parent.payment_term, 'd_name': parent.name, 'd_origine': parent.origin, 'd_address_contact_id': parent.address_contact_id, 'd_user_id': parent.user_id, 'd_comment': parent.comment, 'd_reference': parent.reference}"
1201+ />
1202+ <field name="analytic_distribution_state_recap" attrs="{'invisible': [('is_allocatable', '=', False)]}"/>
1203+ <field name="analytic_distribution_state" invisible="1"/>
1204+ <field name="have_analytic_distribution_from_header" invisible="1"/>
1205+ <field name="is_allocatable" invisible="1"/>
1206+ <field name="quantity"/>
1207+ <field name="price_unit"/>
1208+ <!-- Removed if subtotal is set -->
1209+ <field name="price_subtotal"/>
1210+ <field name="product_id" on_change="product_id_change(product_id, uos_id, quantity, name, parent.type,
1211+ parent.partner_id, parent.fiscal_position, price_unit, parent.address_invoice_id, parent.currency_id,
1212+ {'company_id': parent.company_id})"/>
1213+ <field invisible="True" name="uos_id"/>
1214+ <field invisible="1" name="inactive_product"/>
1215+ </tree>
1216+ </field>
1217+ <group col="4" colspan="4">
1218+ <button string="Compute Total" name="compute_wizard" type="object" icon='gtk-execute' colspan="4"/>
1219+ <group col="6" colspan="4">
1220+ <button name="invoice_reset_wizard" string="Reset Invoice" icon="gtk-cancel" type="object" attrs="{'invisible':['|', ('partner_id','=',False), ('date_invoice','=',False)]}"/>
1221+ <button name="invoice_create_wizard" string="Validate" icon="terp-camera_test" type="object"/>
1222+ </group>
1223+ </group>
1224+ </page>
1225+ <page string="Other Info">
1226+ <field domain="[('partner_id', '=', partner_id)]" name="partner_bank_id" />
1227+ <field name="company_id" invisible="1" widget="selection" groups="base.group_multi_company"/>
1228+ <newline/>
1229+ <field name="payment_term" widget="selection"/>
1230+ <field name="name"/>
1231+ <newline/>
1232+ <field name="origin" />
1233+ <field domain="[('partner_id','=',partner_id)]" name="address_contact_id" />
1234+ <field name="user_id"/>
1235+ <separator colspan="4" string="Additional Information"/>
1236+ <field colspan="4" name="comment" nolabel="1"/>
1237+ </page>
1238+ </notebook>
1239+ </form>
1240+ </field>
1241+ </record>
1242+ </data>
1243+</openerp>

Subscribers

People subscribed via source and target branches