Merge lp:~fabien-morin/unifield-wm/fm-us-713 into lp:unifield-wm
- fm-us-713
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
UniField Reviewer Team | Pending | ||
Review via email: mp+283064@code.launchpad.net |
Commit message
Description of the change
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> |