Merge lp:~npg-team/openobject-addons/account_voucher_credits_us_npg into lp:openobject-addons

Proposed by Novapoint Group
Status: Rejected
Rejected by: Fabien (Open ERP)
Proposed branch: lp:~npg-team/openobject-addons/account_voucher_credits_us_npg
Merge into: lp:openobject-addons
Diff against target: 4025 lines (+3893/-0)
25 files modified
account_voucher_credits_us/Change Log.txt (+70/-0)
account_voucher_credits_us/__init__.py (+27/-0)
account_voucher_credits_us/__openerp__.py (+47/-0)
account_voucher_credits_us/account_voucher_credits_us/Change Log.txt (+40/-0)
account_voucher_credits_us/account_voucher_credits_us/__init__.py (+18/-0)
account_voucher_credits_us/account_voucher_credits_us/__openerp__.py (+32/-0)
account_voucher_credits_us/account_voucher_credits_us/test/account_voucher.yml (+97/-0)
account_voucher_credits_us/account_voucher_credits_us/test/account_voucher_report.yml (+25/-0)
account_voucher_credits_us/account_voucher_credits_us/test/sales_payment.yml (+89/-0)
account_voucher_credits_us/account_voucher_credits_us/test/sales_receipt.yml (+74/-0)
account_voucher_credits_us/account_voucher_credits_us/voucher.py (+1275/-0)
account_voucher_credits_us/account_voucher_credits_us/voucher_payment_receipt_view.xml (+213/-0)
account_voucher_credits_us/account_voucher_credits_us/wizard/__init__.py (+25/-0)
account_voucher_credits_us/account_voucher_credits_us/wizard/account_post_voucher.py (+73/-0)
account_voucher_credits_us/account_voucher_credits_us/wizard/account_post_voucher.xml (+40/-0)
account_voucher_credits_us/security/ir.model.access.csv (+2/-0)
account_voucher_credits_us/test/account_voucher.yml (+97/-0)
account_voucher_credits_us/test/account_voucher_report.yml (+25/-0)
account_voucher_credits_us/test/sales_payment.yml (+89/-0)
account_voucher_credits_us/test/sales_receipt.yml (+74/-0)
account_voucher_credits_us/voucher.py (+1100/-0)
account_voucher_credits_us/voucher_payment_receipt_view.xml (+220/-0)
account_voucher_credits_us/wizard/__init__.py (+26/-0)
account_voucher_credits_us/wizard/account_post_voucher.py (+75/-0)
account_voucher_credits_us/wizard/account_post_voucher.xml (+40/-0)
To merge this branch: bzr merge lp:~npg-team/openobject-addons/account_voucher_credits_us_npg
Reviewer Review Type Date Requested Status
Olivier Dony (Odoo) policy + quick technical scan Disapprove
Review via email: mp+78431@code.launchpad.net

Description of the change

NovaPoint Group has developed this module to provide an improved ability to manage credit memos and apply them against invoices. Allows users to split credit memos and provides significantly more flexibility than the base OpenERP. Accountants have the control and option to use credit memos as they/or their customers direct. Credit memos can be allocated against a single outstanding invoice, partial allocation, against multiple invoices, etc. With this development users have complete control.

To post a comment you must log in.
Revision history for this message
Fabien (Open ERP) (fp-tinyerp) wrote :

Having this feature is good but the implementation seems not good. It's not normal that the diff have 4000 lines of code, it should have around 200 max.

Revision history for this message
Olivier Dony (Odoo) (odo-openerp) wrote :

Hello,

I would like to explain a little bit more why this merge proposal was rejected, in 2 parts: our policy for merge proposals, then some specific hints.

1. Merge Proposal Acceptance Policy
===================================
There may have been contradicting messages about how and when it is useful to make a merge proposal.
We would like to state this policy very clearly, especially now that extra-addons have been deprecated due to the introduction of OpenERP Apps. So we have now added an official Merge Proposal Acceptance Policy to our contributor documentation, please have a look: http://bit.ly/openerp-contrib-mp

2. Remarks specific to this merge proposal
==========================================
In approximate order of importance:

- if this module is specific to the US (which it seems to be) and would be a dependency of l10n_us, it should be named l10n_us_xxx, shouldn't it?
- it looks like this module is a general purpose extra feature that could be a great extra feature on OpenERP Apps as explained in the policy, but does not need to be included in official addons now
- the size of the merge proposal diff is very large (+3900 lines) compared to its scope:
  + DUPLICATED COPY OF THE MODULE in account_voucher_credits_us subdirectory = x2 diff, except they look slightly different!!!?? Very hard to review..
  + lots of copy/pasted code from original addons, are they all really necessary? You duplicated almost the whole account_voucher module! It was certainly possible to avoid that, this is *very* bad practice
- invalid/deprecated XML attributes in views: group.color, field.view_mode, etc.
- oversized methods, e.g. your account_voucher,action_move_line_create() has hundreds of lines! It needs to be refactored!
- you included YAML tests that seem to test and demo the module (good!), but you copy/pasted some existing tests without changing the record IDs.. this is brittle and not likely to work for very long, if it ever does.
- "hook" methods are discouraged in OpenERP, and should be replaced by smaller, overridable API methods as much as possible (e.g. your "_update_discount" hook method)
- use of deprecated API: as of v6.0 it is possible to make multiple XPATH alteration to an inherited form within a single view, you don't need to make multiple views for that
- non-i18n compatible text in warning (missing _() call): e.g. account_post_voucher
- unclear description of the module in the module manifest
- remaining typos in labels/text, e.g. "Avilable Credits"
- multiple leftover "print" debugging statements
- remaining commented out code and TODOs, should be cleaned up

I hope this helps,

Thanks!

review: Disapprove (policy + quick technical scan)

Unmerged revisions

5304. By Novapoint Group

[Add]: account_voucher_credits_us to manage credit memos against invoices

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'account_voucher_credits_us'
2=== added file 'account_voucher_credits_us/Change Log.txt'
3--- account_voucher_credits_us/Change Log.txt 1970-01-01 00:00:00 +0000
4+++ account_voucher_credits_us/Change Log.txt 2011-10-06 15:24:58 +0000
5@@ -0,0 +1,70 @@
6+===============================================================================
7+ Version Change Log (account_voucher_credits_us)
8+===============================================================================
9+1.17 (2011-09-29) -> Arif
10+ * Hide Post button in Credit Card Pay Invoice
11+
12+1.16 (2011-09-16) -> Arif
13+ * Added dicount check box on customer pay invoice
14+ * Removed the onchange on amount in Pay Invoice
15+
16+1.15 (2011-09-15) -> Sinoj & Arif
17+ * Fixed the bug on calculation of discount on Pay Invoice
18+
19+1.13 -> 1.14 (2011-05-24) By Jabir
20+ * Fixed credits are not using on customer payment if no amount is entered Paid Amount
21+
22+1.12 -> 1.13 (2011-04-18) By Jabir
23+ * Created configuration on company to set default payment term for supplier
24+
25+1.11 -> 1.12 (2011-02-07) By Jabir
26+ * Supplier discount support
27+1.10 -> 1.11 (2011-02-07) By Jabir
28+ * Fixed the wrong account posting for journal with type bank and cash
29+
30+1.09 -> 1.10 (2011-02-07) By Jabir
31+ * Removed default focus from Calculate button
32+
33+1.08 -> 1.09 (2011-02-07) By Jabir
34+ * Add write function on account.move.line to make ids list if it is not
35+
36+1.07 -> 1.08 (2011-02-04) By Sinoj
37+ * Optimization and cleanup
38+
39+1.06 -> 1.07 (2010-12-06) By jabir
40+ * Remove payment_meth_id from account_voucher_credits_us and add code to check whether this variable exist before using it
41+
42+1.05 -> 1.06 (2010-12-06) By jabir
43+ * Change the domain of writeoff_account and remove extra whitespace and comments in voucher.py
44+
45+1.04 -> 1.05 (2010-12-06) By jabir
46+ * Take invoice partner instead of voucher partner
47+
48+1.03 -> 1.04 (2010-11-24) By jabir
49+ * Fixed unable to open new customer payment form issue occur due to trunk update
50+ * Fixed onchange_pay function error occuring when writeoff or discount module uninstalled
51+
52+1.02 -> 1.03 (2010-11-24) By jabir
53+ * Select credit and debit lines of child partner along with its when a customer is selected
54+ * Automatically fill payment amount when Pay is checked in voucher lines
55+
56+1.01 -> 1.02 (2010-11-12) By jabir
57+ * Add all the feature in module account_voucher_jdc in this module
58+ ===============================================================================
59+ Version Change Log (account_voucher_jdc)
60+ ===============================================================================
61+ 1.01 -> 1.02 (2010-11-09) By sinoj
62+ * in "account.voucher.line", label string "Discount and Credit" changed to "Discount and Credits"
63+
64+ 1.0 -> 1.01 (2010-11-03) By sinoj
65+ * in "account.voucher.line", label string for amount_original changed from "Original Amount" to "Original Amt."
66+ * in "account.voucher.line", label string for amount_unreconciled changed from "Open Balance" to "Amount Due"
67+ * in "account.voucher.line", label string for amount changed from "Amount" to "Payment Amt"
68+ * in "account.voucher.line", label string for date_original changed from "Date" to "Invoice Date"
69+ * in "account.voucher.line", label string for account_id changed from "Account" to "G/L Account"
70+ * in "account.voucher.line", New boolean field Pay
71+
72+===============================================================================
73+ Version Change Log (account_voucher_credits_jdc)
74+===============================================================================
75+ account_voucher_jdc 1.0 -> account_voucher_credits_jdc 0.01 (2010-11-03) By sinoj
76
77=== added file 'account_voucher_credits_us/__init__.py'
78--- account_voucher_credits_us/__init__.py 1970-01-01 00:00:00 +0000
79+++ account_voucher_credits_us/__init__.py 2011-10-06 15:24:58 +0000
80@@ -0,0 +1,27 @@
81+# -*- coding: utf-8 -*-
82+##############################################################################
83+#
84+# OpenERP, Open Source Management Solution
85+# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
86+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
87+#
88+# This program is free software: you can redistribute it and/or modify
89+# it under the terms of the GNU General Public License as published by
90+# the Free Software Foundation, either version 3 of the License, or
91+# (at your option) any later version.
92+#
93+# This program is distributed in the hope that it will be useful,
94+# but WITHOUT ANY WARRANTY; without even the implied warranty of
95+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
96+# GNU General Public License for more details.
97+#
98+# You should have received a copy of the GNU General Public License
99+# along with this program. If not, see <http://www.gnu.org/licenses/>
100+#
101+##############################################################################
102+
103+import voucher
104+import wizard
105+
106+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
107+
108
109=== added file 'account_voucher_credits_us/__openerp__.py'
110--- account_voucher_credits_us/__openerp__.py 1970-01-01 00:00:00 +0000
111+++ account_voucher_credits_us/__openerp__.py 2011-10-06 15:24:58 +0000
112@@ -0,0 +1,47 @@
113+# -*- coding: utf-8 -*-
114+##############################################################################
115+#
116+# OpenERP, Open Source Management Solution
117+# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
118+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
119+#
120+# This program is free software: you can redistribute it and/or modify
121+# it under the terms of the GNU General Public License as published by
122+# the Free Software Foundation, either version 3 of the License, or
123+# (at your option) any later version.
124+#
125+# This program is distributed in the hope that it will be useful,
126+# but WITHOUT ANY WARRANTY; without even the implied warranty of
127+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
128+# GNU General Public License for more details.
129+#
130+# You should have received a copy of the GNU General Public License
131+# along with this program. If not, see <http://www.gnu.org/licenses/>
132+#
133+##############################################################################
134+
135+{
136+ "name" : "Voucher modifications for US",
137+ "version" : "1.17",
138+ "author" : 'NovaPoint Group LLC',
139+ "description": """
140+This module will add new functionality to better manage credits and how and when they apply to Sales Payments.
141+The result of this development will enable users to designate which credit(s) will apply to each individual invoice, and how much of an individual credit amount to apply.
142+ """,
143+ "category" : "US Localisation/Account",
144+ "website" : "http://www.novapointgroup.com/",
145+ "depends" : ["account", "account_voucher",],
146+ "init_xml" : [],
147+
148+ "demo_xml" : [],
149+
150+ "update_xml" : [
151+ "voucher_payment_receipt_view.xml",
152+ "wizard/account_post_voucher.xml",
153+ "security/ir.model.access.csv"],
154+ "test" : [],
155+ "active": False,
156+ "installable": True,
157+ }
158+
159+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
160
161=== added directory 'account_voucher_credits_us/account_voucher_credits_us'
162=== added file 'account_voucher_credits_us/account_voucher_credits_us/Change Log.txt'
163--- account_voucher_credits_us/account_voucher_credits_us/Change Log.txt 1970-01-01 00:00:00 +0000
164+++ account_voucher_credits_us/account_voucher_credits_us/Change Log.txt 2011-10-06 15:24:58 +0000
165@@ -0,0 +1,40 @@
166+
167+===============================================================================
168+ Version Change Log (account_voucher_credits_us)
169+===============================================================================
170+1.06 -> 1.07 (2010-12-06) By jabir
171+ * Remove payment_meth_id from account_voucher_credits_us and add code to check whether this variable exist before using it
172+1.05 -> 1.06 (2010-12-06) By jabir
173+ * Change the domain of writeoff_account and remove extra whitespace and comments in voucher.py
174+1.04 -> 1.05 (2010-12-06) By jabir
175+ * Take invoice partner instead of voucher partner
176+1.03 -> 1.04 (2010-11-24) By jabir
177+ * Fixed unable to open new customer payment form issue occur due to trunk update
178+ * Fixed onchange_pay function error occuring when writeoff or discount module uninstalled
179+
180+1.02 -> 1.03 (2010-11-24) By jabir
181+ * Select credit and debit lines of child partner along with its when a customer is selected
182+ * Automatically fill payment amount when Pay is checked in voucher lines
183+
184+1.01 -> 1.02 (2010-11-12) By jabir
185+ * Add all the feature in module account_voucher_jdc in this module
186+ ===============================================================================
187+ Version Change Log (account_voucher_jdc)
188+ ===============================================================================
189+ 1.01 -> 1.02 (2010-11-09) By sinoj
190+ * in "account.voucher.line", label string "Discount and Credit" changed to "Discount and Credits"
191+
192+ 1.0 -> 1.01 (2010-11-03) By sinoj
193+ * in "account.voucher.line", label string for amount_original changed from "Original Amount" to "Original Amt."
194+ * in "account.voucher.line", label string for amount_unreconciled changed from "Open Balance" to "Amount Due"
195+ * in "account.voucher.line", label string for amount changed from "Amount" to "Payment Amt"
196+ * in "account.voucher.line", label string for date_original changed from "Date" to "Invoice Date"
197+ * in "account.voucher.line", label string for account_id changed from "Account" to "G/L Account"
198+ * in "account.voucher.line", New boolean field Pay
199+
200+===============================================================================
201+ Version Change Log (account_voucher_credits_jdc)
202+===============================================================================
203+ account_voucher_jdc 1.0 -> account_voucher_credits_jdc 0.01 (2010-11-03) By sinoj
204+
205+
206
207=== added file 'account_voucher_credits_us/account_voucher_credits_us/__init__.py'
208--- account_voucher_credits_us/account_voucher_credits_us/__init__.py 1970-01-01 00:00:00 +0000
209+++ account_voucher_credits_us/account_voucher_credits_us/__init__.py 2011-10-06 15:24:58 +0000
210@@ -0,0 +1,18 @@
211+# -*- encoding: utf-8 -*-
212+##############################################################################
213+#
214+# Copyright (c) 2010 ZestyBeanz Technologies Pvt. Ltd.
215+# (http://wwww.zbeanztech.com) All Rights Reserved.
216+# sinoj@zbeanztech.com
217+#
218+##############################################################################
219+
220+'''
221+ TODO : Add access rules for all objects in the module
222+
223+'''
224+import voucher
225+import wizard
226+
227+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
228+
229
230=== added file 'account_voucher_credits_us/account_voucher_credits_us/__openerp__.py'
231--- account_voucher_credits_us/account_voucher_credits_us/__openerp__.py 1970-01-01 00:00:00 +0000
232+++ account_voucher_credits_us/account_voucher_credits_us/__openerp__.py 2011-10-06 15:24:58 +0000
233@@ -0,0 +1,32 @@
234+# -*- encoding: utf-8 -*-
235+##############################################################################
236+#
237+# Copyright (c) 2010 ZestyBeanz Technologies Pvt. Ltd.
238+# (http://wwww.zbeanztech.com) All Rights Reserved.
239+# sinoj@zbeanztech.com
240+#
241+##############################################################################
242+{
243+ "name" : "Voucher modifications for US",
244+ "version" : "0.07",
245+ "author" : 'Voucher and NovaPoint Group LLC',
246+ "description": """
247+This module will add new functionality to better manage credits and how and when they apply to Sales Payments.
248+The result of this development will enable users to designate which credit(s) will apply to each individual invoice, and how much of an individual credit amount to apply.
249+ """,
250+ "category" : "Generic Modules/Accounting",
251+ "website" : "http://www.novapointgroup.com/",
252+ "depends" : ["account", "account_voucher",],
253+ "init_xml" : [],
254+
255+ "demo_xml" : [],
256+
257+ "update_xml" : [
258+ "voucher_payment_receipt_view.xml",
259+ "wizard/account_post_voucher.xml",],
260+ "test" : [],
261+ "active": False,
262+ "installable": True,
263+ }
264+
265+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
266
267=== added directory 'account_voucher_credits_us/account_voucher_credits_us/test'
268=== added file 'account_voucher_credits_us/account_voucher_credits_us/test/account_voucher.yml'
269--- account_voucher_credits_us/account_voucher_credits_us/test/account_voucher.yml 1970-01-01 00:00:00 +0000
270+++ account_voucher_credits_us/account_voucher_credits_us/test/account_voucher.yml 2011-10-06 15:24:58 +0000
271@@ -0,0 +1,97 @@
272+
273+-
274+ In order to check account voucher module in OpenERP I create a customer voucher
275+-
276+ !record {model: account.voucher, id: account_voucher_voucherforaxelor0}:
277+ account_id: account.cash
278+ company_id: base.main_company
279+ currency_id: base.EUR
280+ journal_id: account.bank_journal
281+ name: Voucher for Axelor
282+ narration: Basic Pc
283+ line_ids:
284+ - account_id: account.a_recv
285+ amount: 1000.0
286+ name: Voucher for Axelor
287+ partner_id: base.res_partner_desertic_hispafuentes
288+ period_id: account.period_6
289+ reference_type: none
290+
291+-
292+ I check that Initially customer voucher is in the "Draft" state
293+-
294+ !assert {model: account.voucher, id: account_voucher_voucherforaxelor0}:
295+ - state == 'draft'
296+-
297+ I compute the voucher to calculate the taxes by clicking Cpmpute button
298+-
299+ !workflow {model: account.voucher, action: proforma_voucher, ref: account_voucher_voucherforaxelor0}
300+-
301+ I check that the voucher state is now "proforma"
302+-
303+ !assert {model: account.voucher, id: account_voucher_voucherforaxelor0}:
304+ - state == 'proforma'
305+-
306+ I create voucher by clicking on Create button
307+-
308+ !workflow {model: account.voucher, action: proforma_voucher, ref: account_voucher_voucherforaxelor0}
309+-
310+ I clicked on Validate Button
311+-
312+ !assert {model: account.voucher, id: account_voucher_voucherforaxelor0}:
313+ - state == 'posted'
314+
315+-
316+ I check that Moves get created for this voucher
317+-
318+ !python {model: account.voucher}: |
319+ acc_id=self.browse(cr, uid, ref("account_voucher_voucherforaxelor0"))
320+ assert(acc_id.move_id)
321+
322+
323+-
324+ Now I create a Vendor Voucher
325+-
326+ !record {model: account.voucher, id: account_voucher_voucheraxelor0}:
327+ account_id: account.cash
328+ company_id: base.main_company
329+ currency_id: base.EUR
330+ journal_id: account.bank_journal
331+ name: Voucher Axelor
332+ narration: Basic PC
333+ line_ids:
334+ - account_id: account.cash
335+ amount: 1000.0
336+ name: Voucher Axelor
337+ period_id: account.period_6
338+ reference_type: none
339+
340+-
341+ I check that Initially vendor voucher is in the "Draft" state
342+-
343+ !assert {model: account.voucher, id: account_voucher_voucheraxelor0}:
344+ - state == 'draft'
345+-
346+ I change the state of voucher to "proforma" by clicking PRO-FORMA button
347+-
348+ !workflow {model: account.voucher, action: proforma_voucher, ref: account_voucher_voucheraxelor0}
349+-
350+ I check that the voucher state is now "proforma"
351+-
352+ !assert {model: account.voucher, id: account_voucher_voucheraxelor0}:
353+ - state == 'proforma'
354+-
355+ I create voucher by clicking on Create button
356+-
357+ !workflow {model: account.voucher, action: proforma_voucher, ref: account_voucher_voucheraxelor0}
358+-
359+ I check that the voucher state is "posted"
360+-
361+ !assert {model: account.voucher, id: account_voucher_voucheraxelor0}:
362+ - state == 'posted'
363+-
364+ I check that moves get created for this voucher
365+-
366+ !python {model: account.voucher}: |
367+ acc_id=self.browse(cr, uid, ref("account_voucher_voucheraxelor0"))
368+ assert(acc_id.move_id)
369
370=== added file 'account_voucher_credits_us/account_voucher_credits_us/test/account_voucher_report.yml'
371--- account_voucher_credits_us/account_voucher_credits_us/test/account_voucher_report.yml 1970-01-01 00:00:00 +0000
372+++ account_voucher_credits_us/account_voucher_credits_us/test/account_voucher_report.yml 2011-10-06 15:24:58 +0000
373@@ -0,0 +1,25 @@
374+-
375+ Demo for Account Voucher
376+-
377+ !record {model: account.voucher, id: account_voucher_voucheraxelor0}:
378+ account_id: account.cash
379+ company_id: base.main_company
380+ currency_id: base.EUR
381+ journal_id: account.bank_journal
382+ name: Voucher Axelor
383+ narration: Basic PC
384+ amount: 1000.0
385+ line_ids:
386+ - account_id: account.cash
387+ amount: 1000.0
388+ name: Voucher Axelor
389+ period_id: account.period_6
390+
391+-
392+ In order to test the PDF reports defined on a account_voucher, we will print account voucher Report
393+-
394+ !python {model: account.voucher}: |
395+ import netsvc, tools, os
396+ (data, format) = netsvc.LocalService('report.voucher.cash_receipt.drcr').create(cr, uid, [ref("account_voucher_voucheraxelor0")], {}, {})
397+ if tools.config['test_report_directory']:
398+ file(os.path.join(tools.config['test_report_directory'], 'account_voucher-report.'+format), 'wb+').write(data)
399
400=== added file 'account_voucher_credits_us/account_voucher_credits_us/test/sales_payment.yml'
401--- account_voucher_credits_us/account_voucher_credits_us/test/sales_payment.yml 1970-01-01 00:00:00 +0000
402+++ account_voucher_credits_us/account_voucher_credits_us/test/sales_payment.yml 2011-10-06 15:24:58 +0000
403@@ -0,0 +1,89 @@
404+-
405+ In order to test account voucher i will create an invoice and pay it through account voucher.
406+-
407+ I create a new Partner
408+-
409+ !record {model: res.partner, id: res_partner_micropc0}:
410+ address:
411+ - country_id: base.be
412+ name: Jenifer
413+ street: 69 rue de Chimay
414+ type: default
415+ zip: '5478'
416+ category_id:
417+ - base.res_partner_category_8
418+ credit_limit: 0.0
419+ name: Micro PC
420+ property_account_payable: account.a_pay
421+ property_account_receivable: account.a_recv
422+
423+-
424+ Create an invoice for the partner
425+-
426+ !record {model: account.invoice, id: account_invoice_0}:
427+ account_id: account.a_recv
428+ address_contact_id: base.res_partner_address_7
429+ address_invoice_id: base.res_partner_address_7
430+ company_id: base.main_company
431+ currency_id: base.EUR
432+ invoice_line:
433+ - account_id: account.a_sale
434+ name: '[PC1] Basic PC'
435+ price_unit: 450.0
436+ quantity: 1.0
437+ product_id: product.product_product_pc1
438+ uos_id: product.product_uom_unit
439+ journal_id: account.sales_journal
440+ partner_id: res_partner_micropc0
441+
442+-
443+ I check that the customer invoice is in draft state
444+-
445+ !assert {model: account.invoice, id: account_invoice_0}:
446+ - state == 'draft'
447+
448+-
449+ I make the invoice in Open state
450+-
451+ !workflow {model: account.invoice, action: invoice_open, ref: account_invoice_0}
452+
453+-
454+ I check that a payment entry gets created in the account.move.line
455+-
456+ !python {model: account.invoice}: |
457+ acc_id=self.browse(cr, uid, ref("account_invoice_0"))
458+ assert(acc_id.move_id)
459+
460+-
461+ I will create and post an account voucher for the partner.
462+-
463+ !python {model: account.voucher}: |
464+ import netsvc
465+ vals = {}
466+ journal_id = self.default_get(cr, uid, ['journal_id']).get('journal_id',None)
467+ res = self.onchange_partner_id(cr, uid, [], ref("res_partner_micropc0"), journal_id, price=0.0, ttype='receipt')
468+ vals = {
469+ 'account_id': ref('account.cash'),
470+ 'amount': 450.0,
471+ 'company_id': ref('base.main_company'),
472+ 'currency_id': ref('base.EUR'),
473+ 'journal_id': ref('account.bank_journal'),
474+ 'partner_id': ref('res_partner_micropc0'),
475+ 'period_id': ref('account.period_8'),
476+ 'type': 'receipt',
477+ }
478+ if not res['value']['line_cr_ids']:
479+ res['value']['line_cr_ids'] = [{'type': 'cr', 'account_id': ref('account.a_recv'),}]
480+ res['value']['line_cr_ids'][0]['amount'] = 450.0
481+ vals['line_cr_ids'] = [(0,0,i) for i in res['value']['line_cr_ids']]
482+ id = self.create(cr, uid, vals)
483+ voucher_id = self.browse(cr, uid, id)
484+ assert (voucher_id.state=='draft'), "Voucher is not in draft state"
485+ wf_service = netsvc.LocalService("workflow")
486+ wf_service.trg_validate(uid, 'account.voucher', voucher_id.id, 'proforma_voucher', cr)
487+
488+-
489+ Finally i will Confirm the state of the invoice is paid
490+-
491+ !assert {model: account.invoice, id: account_invoice_0}:
492+ - state == 'paid'
493
494=== added file 'account_voucher_credits_us/account_voucher_credits_us/test/sales_receipt.yml'
495--- account_voucher_credits_us/account_voucher_credits_us/test/sales_receipt.yml 1970-01-01 00:00:00 +0000
496+++ account_voucher_credits_us/account_voucher_credits_us/test/sales_receipt.yml 2011-10-06 15:24:58 +0000
497@@ -0,0 +1,74 @@
498+
499+-
500+ In order to test sales receipt i will create a sale receipt and pay it through sales payment
501+-
502+ First of all I create a voucher
503+-
504+ !record {model: account.voucher, id: account_voucher_chinaexport_0}:
505+ account_id: account.a_recv
506+ amount: 30000.0
507+ company_id: base.main_company
508+ journal_id: account.sales_journal
509+ line_cr_ids:
510+ - account_id: account.a_sale
511+ amount: 30000.0
512+ partner_id: base.res_partner_3
513+ period_id: account.period_8
514+ tax_amount: 0.0
515+ type: sale
516+
517+-
518+ I check that the voucher state is Draft
519+-
520+ !assert {model: account.voucher, id: account_voucher_chinaexport_0}:
521+ - state == 'draft'
522+
523+-
524+ I clicked on post button to post the voucher
525+-
526+ !workflow {model: account.voucher, action: proforma_voucher, ref: account_voucher_chinaexport_0}
527+
528+-
529+ Check the voucher state is Posted
530+-
531+ !assert {model: account.voucher, id: account_voucher_chinaexport_0}:
532+ - state == 'posted'
533+
534+-
535+ I create a voucher record for the same partner
536+-
537+ !record {model: account.voucher, id: account_voucher_chinaexport_1}:
538+ account_id: account.cash
539+ amount: 30000.0
540+ company_id: base.main_company
541+ currency_id: base.EUR
542+ journal_id: account.bank_journal
543+ line_cr_ids:
544+ - account_id: account.a_recv
545+ amount: 0.0
546+ name: 2010/003
547+ type: cr
548+ - account_id: account.a_recv
549+ amount: 30000.0
550+ name: 2010/003
551+ type: cr
552+ partner_id: base.res_partner_3
553+ period_id: account.period_8
554+ type: receipt
555+
556+-
557+ Check the voucher state is draft
558+-
559+ !assert {model: account.voucher, id: account_voucher_chinaexport_1}:
560+ - state == 'draft'
561+
562+-
563+ I clicked on Post button to post the voucher
564+-
565+ !workflow {model: account.voucher, action: proforma_voucher, ref: account_voucher_chinaexport_1}
566+
567+-
568+ Check the voucher state is Posted
569+-
570+ !assert {model: account.voucher, id: account_voucher_chinaexport_1}:
571+ - state == 'posted'
572
573=== added file 'account_voucher_credits_us/account_voucher_credits_us/voucher.py'
574--- account_voucher_credits_us/account_voucher_credits_us/voucher.py 1970-01-01 00:00:00 +0000
575+++ account_voucher_credits_us/account_voucher_credits_us/voucher.py 2011-10-06 15:24:58 +0000
576@@ -0,0 +1,1275 @@
577+# -*- coding: utf-8 -*-
578+##############################################################################
579+#
580+# OpenERP, Open Source Management Solution
581+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
582+#
583+# This program is free software: you can redistribute it and/or modify
584+# it under the terms of the GNU Affero General Public License as
585+# published by the Free Software Foundation, either version 3 of the
586+# License, or (at your option) any later version.
587+#
588+# This program is distributed in the hope that it will be useful,
589+# but WITHOUT ANY WARRANTY; without even the implied warranty of
590+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
591+# GNU Affero General Public License for more details.
592+#
593+# You should have received a copy of the GNU Affero General Public License
594+# along with this program. If not, see <http://www.gnu.org/licenses/>.
595+#
596+##############################################################################
597+
598+import time
599+import netsvc
600+from osv import fields
601+from osv import osv
602+from tools.translate import _
603+def _combinations(iterable, r):
604+ '''
605+ @return: combination generator object
606+
607+ Example
608+ combinations(’ABCD’, 2) --> AB AC AD BC BD CD
609+ combinations(range(4), 3) --> 012 013 023 123
610+ '''
611+ pool = tuple(iterable)
612+ n = len(pool)
613+ if r > n:
614+ return
615+ indices = range(r)
616+ yield tuple(pool[i] for i in indices)
617+ while True:
618+ for i in reversed(range(r)):
619+ if indices[i] != i + n - r:
620+ break
621+ else:
622+ return
623+ indices[i] += 1
624+ for j in range(i+1, r):
625+ indices[j] = indices[j-1] + 1
626+ yield tuple(pool[i] for i in indices)
627+
628+
629+class res_company(osv.osv):
630+ _name = 'res.company'
631+ _inherit = 'res.company'
632+
633+ _columns = {
634+ 'writeoff_account': fields.many2one('account.account', 'Writeoff Account', domain=[('type','!=','view'),('type','!=','consolidation')],
635+ help="This is the designated write-off gl account that will be used when writing off remaining amounts in customer payment."),
636+ }
637+res_company()
638+
639+
640+class account_cash_discount(osv.osv):
641+ _name = "account.cash.discount"
642+ _description = "Cash Discount" #A reduction in the price if payment is made within a stipulated period.
643+ _columns = {
644+ 'name': fields.char('Name', size=32),
645+ 'delay': fields.integer('Number of Days', required=True),
646+ 'discount': fields.float('Discount (%)', digits=(16,6),required=True),
647+ 'payment_id': fields.many2one('account.payment.term','Associated Payment Term'),
648+ 'credit_account_id': fields.many2one('account.account', 'Credit Account'),
649+ 'debit_account_id': fields.many2one('account.account', 'Debit Account'),
650+ }
651+account_cash_discount()
652+
653+
654+
655+class account_move_line(osv.osv):
656+ _inherit = 'account.move.line'
657+ def _unreconciled(self, cr, uid, ids, prop, unknow_none, context):
658+ '''
659+ Function to calculate the value of variable Unreconciled Amount
660+ '''
661+ res={}
662+ for line in self.browse(cr, uid, ids, context=context):
663+ res[line.id] = line.debit - line.credit
664+ if line.reconcile_partial_id:
665+ res[line.id] = 0
666+ for partial in line.reconcile_partial_id.line_partial_ids:
667+ res[line.id] += partial.debit - partial.credit
668+ res[line.id] = abs(res[line.id])
669+ return res
670+
671+ _columns = {
672+ 'amount_unreconciled': fields.function(_unreconciled, method=True, string='Unreconciled Amount'),
673+ }
674+account_move_line()
675+
676+class account_voucher_line(osv.osv):
677+ _inherit = 'account.voucher.line'
678+
679+
680+ def _update_credit_lines(self,cr, uid, ids, context):
681+ credits_used_pool = self.pool.get('account.voucher.line.credits_to_use')
682+ for line in self.browse(cr , uid, ids, context):
683+ credits_lines_used = [x.orginal_credit_line_id.id for x in line.available_credits]
684+ for credit_line in line.voucher_id.line_dr_ids :
685+ if credit_line.id not in credits_lines_used and line.invoice_id and line.invoice_id.payment_term:
686+ print credit_line
687+ credits_used_pool.create(cr, uid, {
688+ 'voucher_line_id': line.id,
689+ 'orginal_credit_line_id':credit_line.id,
690+ 'use_credit': False,
691+ 'inv_credit': credit_line.move_line_id.id,
692+ 'discount_window_date': credit_line.date_original,
693+ 'orginal_amount': credit_line.amount_original,
694+ 'available_amount': credit_line.amount_unreconciled - credit_line.pending_credits,
695+ 'discount_amount': 0.0,
696+ 'gl_account' : credit_line.account_id.id,})
697+ else :
698+ to_update_credit_line_ids = credits_used_pool.search(cr,uid,[('voucher_line_id','=',line.id),( 'orginal_credit_line_id','=',credit_line.id)], context=context)
699+ credits_used_pool.write(cr, uid,to_update_credit_line_ids,{'available_amount': credit_line.amount_unreconciled-credit_line.pending_credits}, context=context)
700+
701+account_voucher_line()
702+
703+class account_voucher(osv.osv):
704+ def _get_type(self, cr, uid, ids, context={}):
705+ '''
706+ Initialise the variable Default Type
707+ '''
708+ return context.get('type', False)
709+
710+ def _get_period(self, cr, uid, context={}):
711+ '''
712+ Initialise the variable Period
713+ '''
714+
715+ if context.get('period_id', False):
716+ return context.get('period_id')
717+ periods = self.pool.get('account.period').find(cr, uid)
718+ return periods and periods[0] or False
719+
720+ def _get_journal(self, cr, uid, context={}):
721+ '''
722+ Initialise the variable Journal
723+ '''
724+ journal_pool = self.pool.get('account.journal')
725+ if context.get('journal_id', False):
726+ return context.get('journal_id')
727+ if not context.get('journal_id', False) and context.get('search_default_journal_id', False):
728+ return context.get('search_default_journal_id')
729+
730+ ttype = context.get('type', 'bank')
731+ res = journal_pool.search(cr, uid, [('type', '=', ttype)], limit=1)
732+ return res and res[0] or False
733+
734+ def _get_tax(self, cr, uid, context={}):
735+ '''
736+ Initialise the variable Tax
737+ '''
738+ journal_pool = self.pool.get('account.journal')
739+ journal_id = context.get('journal_id', False)
740+ if not journal_id:
741+ ttype = context.get('type', 'bank')
742+ res = journal_pool.search(cr, uid, [('type', '=', ttype)], limit=1)
743+ if not res:
744+ return False
745+ journal_id = res[0]
746+
747+ if not journal_id:
748+ return False
749+ journal = journal_pool.browse(cr, uid, journal_id)
750+ account_id = journal.default_credit_account_id or journal.default_debit_account_id
751+ if account_id and account_id.tax_ids:
752+ tax_id = account_id.tax_ids[0].id
753+ return tax_id
754+ return False
755+
756+ def _get_currency(self, cr, uid, context):
757+ '''
758+ Initialise the variable Currency
759+ '''
760+ journal_pool = self.pool.get('account.journal')
761+ journal_id = context.get('journal_id', False)
762+ if journal_id:
763+ journal = journal_pool.browse(cr, uid, journal_id)
764+ currency_id = journal.company_id.currency_id.id
765+ if journal.currency:
766+ currency_id = journal.currency.id
767+ return False
768+
769+ def _get_partner(self, cr, uid, context={}):
770+ '''
771+ Initialise the variable Partner
772+ '''
773+ return context.get('partner_id', False)
774+
775+ def _get_reference(self, cr, uid, context={}):
776+ '''
777+ Initialise the variable Ref #
778+ '''
779+ return context.get('reference', False)
780+
781+ def _get_narration(self, cr, uid, context={}):
782+ '''
783+ Initialise the field Notes
784+ '''
785+ return context.get('narration', False)
786+
787+
788+ def _find_exact_match(self, cr, uid, lines, amount, mark_pay=False, use_discount=False, context=False):
789+ '''
790+ accept voucher lines as list of dict and amount to match
791+ return : update lines if it found an exact match
792+ '''
793+ total_amount = 0.0
794+ discount_available = {}
795+ total_discount_available = 0.0
796+ for line in lines:
797+ if mark_pay or line.pay == True:
798+ if use_discount:
799+ discount_available[line.id] = 0.0
800+ for discount_line in line.available_discounts:
801+ discount_available[line.id] += discount_line.proposed_discount
802+ total_discount_available += discount_line.proposed_discount
803+ total_amount += line.amount_unreconciled - line.credit_used - (line._columns.has_key('writeoff_amount') and line.writeoff_amount)
804+ else:
805+ total_amount += line.amount_unreconciled - line.credit_used - line.discount_used- (line._columns.has_key('writeoff_amount') and line.writeoff_amount)
806+ if abs(amount - total_amount) < 0.01 :
807+ '''
808+ Unchecking all the lines other than matched line
809+ '''
810+ line_ids = self.read(cr,uid,lines[0].voucher_id.id,['line_ids'])['line_ids']
811+ self.pool.get('account.voucher.line').write(cr,uid,line_ids,{'pay':False})
812+ for line in lines:
813+ if mark_pay or line.pay == True:
814+ line.write({'amount':line.amount_unreconciled - line.credit_used - line.discount_used - (line._columns.has_key('writeoff_amount') and line.writeoff_amount), 'pay':True })
815+ else:
816+ line.write({'amount':0.0, 'pay':False })
817+ return True
818+ if use_discount and total_amount>amount and (total_amount - amount ) <=total_discount_available: #It is possible to match using Discount
819+ '''
820+ Unchecking all the lines other than matched line and uncheck all discount lines
821+ '''
822+ line_ids = self.read(cr,uid,lines[0].voucher_id.id,['line_ids'])['line_ids']
823+ self.pool.get('account.voucher.line').write(cr,uid,line_ids,{'pay':False})
824+ for line_id in line_ids:
825+ discount_ids = self.pool.get('account.voucher.line').read(cr,uid,line_id,['available_discounts'])['available_discounts']
826+ self.pool.get("account.voucher.line.discount_to_use").write(cr,uid,discount_ids,{'use_discount':False, 'discount_amount':0.0})
827+ discount_to_use = total_amount - amount
828+ for line in lines:
829+ if discount_to_use > 0.0:
830+ for discount_line in line.available_discounts:
831+ discount_line.write({'use_discount':True,'discount_amount':min(discount_to_use, discount_line.proposed_discount)})
832+ discount_to_use = discount_to_use - min(discount_to_use, discount_line.proposed_discount)
833+ line = self.pool.get('account.voucher.line').browse(cr, uid, line.id,context=context)
834+ line.write({'amount':line.amount_unreconciled - line.credit_used - line.discount_used - (line._columns.has_key('writeoff_amount') and line.writeoff_amount), 'pay':True })
835+ return True
836+ if len(lines) > 1:
837+ for combination in _combinations(lines,len(lines)-1 ):
838+ if self._find_exact_match(cr, uid, combination, amount, mark_pay, context=False):
839+ return True
840+ return False
841+ def calc_diff(self, cr, uid, ids, context={}):
842+ res = {'nodestroy':True}
843+ amount_cash_discount = 0.0
844+ amount_interest = 0.0
845+ for vch in self.browse(cr, uid, ids):
846+ for line in vch.line_cr_ids:
847+ line._update_credit_lines( context=context)
848+ if vch.auto_match:
849+ ret = self._find_exact_match(cr, uid, vch.line_cr_ids, vch.amount, mark_pay=False, use_discount=False, context=False)
850+ if not ret:
851+ ret = self._find_exact_match(cr, uid, vch.line_cr_ids, vch.amount, mark_pay=True, use_discount=False, context=False)
852+ if not ret:
853+ ret = self._find_exact_match(cr, uid, vch.line_cr_ids, vch.amount, mark_pay=True, use_discount=True, context=False)
854+ return res
855+
856+ _name = 'account.voucher'
857+ _inherit = 'account.voucher'
858+ _description = 'Accounting Voucher'
859+ _order = "date desc, id desc"
860+ _rec_name = 'number'
861+ _columns = {
862+ 'type':fields.selection([
863+ ('sale','Sale'),
864+ ('purchase','Purchase'),
865+ ('payment','Payment'),
866+ ('receipt','Receipt'),
867+ ],'Default Type', readonly=True, states={'draft':[('readonly',False)]}),
868+ 'name':fields.char('Memo', size=256, readonly=True, states={'draft':[('readonly',False)]}),
869+ 'date':fields.date('Date', readonly=True, states={'draft':[('readonly',False)]}, help="Effective date for accounting entries"),
870+ 'journal_id':fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}),
871+ 'account_id':fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}),
872+ 'line_ids':fields.one2many('account.voucher.line','voucher_id','Voucher Lines', readonly=True, states={'draft':[('readonly',False)]}),
873+ 'line_cr_ids':fields.one2many('account.voucher.line','voucher_id','Credits',
874+ domain=[('type','=','cr')], context={'default_type':'cr'}, readonly=True, states={'draft':[('readonly',False)]}),
875+ 'line_dr_ids':fields.one2many('account.voucher.line','voucher_id','Debits',
876+ domain=[('type','=','dr')], context={'default_type':'dr'}, readonly=True, states={'draft':[('readonly',False)]}),
877+ 'period_id': fields.many2one('account.period', 'Period', required=True, readonly=True, states={'draft':[('readonly',False)]}),
878+ 'narration':fields.text('Notes', readonly=True, states={'draft':[('readonly',False)]}),
879+ 'currency_id':fields.many2one('res.currency', 'Currency', readonly=True, states={'draft':[('readonly',False)]}),
880+# 'currency_id': fields.related('journal_id','currency', type='many2one', relation='res.currency', string='Currency', store=True, readonly=True, states={'draft':[('readonly',False)]}),
881+ 'company_id': fields.many2one('res.company', 'Company', required=True, readonly=True, states={'draft':[('readonly',False)]}),
882+ 'state':fields.selection(
883+ [('draft','Draft'),
884+ ('proforma','Pro-forma'),
885+ ('posted','Posted'),
886+ ('cancel','Cancelled')
887+ ], 'State', readonly=True, size=32,
888+ help=' * The \'Draft\' state is used when a user is encoding a new and unconfirmed Voucher. \
889+ \n* The \'Pro-forma\' when voucher is in Pro-forma state,voucher does not have an voucher number. \
890+ \n* The \'Posted\' state is used when user create voucher,a voucher number is generated and voucher entries are created in account \
891+ \n* The \'Cancelled\' state is used when user cancel voucher.'),
892+ 'amount': fields.float('Total', digits=(16, 2), required=True, readonly=True, states={'draft':[('readonly',False)]}),
893+ 'tax_amount':fields.float('Tax Amount', digits=(14,2), readonly=True, states={'draft':[('readonly',False)]}),
894+ 'reference': fields.char('Ref #', size=64, readonly=True, states={'draft':[('readonly',False)]}, help="Transaction reference number."),
895+ 'number': fields.related('move_id', 'name', type="char", readonly=True, string='Number'),
896+ 'move_id':fields.many2one('account.move', 'Account Entry'),
897+ 'move_ids': fields.related('move_id','line_id', type='one2many', relation='account.move.line', string='Journal Items', readonly=True),
898+ 'partner_id':fields.many2one('res.partner', 'Partner', change_default=1, readonly=True, states={'draft':[('readonly',False)]}),
899+ 'audit': fields.related('move_id','to_check', type='boolean', relation='account.move', string='Audit Complete ?'),
900+ 'pay_now':fields.selection([
901+ ('pay_now','Pay Directly'),
902+ ('pay_later','Pay Later or Group Funds'),
903+ ],'Payment', select=True, readonly=True, states={'draft':[('readonly',False)]}),
904+ 'tax_id':fields.many2one('account.tax', 'Tax', readonly=True, states={'draft':[('readonly',False)]}),
905+ 'pre_line':fields.boolean('Previous Payments ?', required=False),
906+ 'date_due': fields.date('Due Date', readonly=True, states={'draft':[('readonly',False)]}),
907+ 'auto_match':fields.boolean('Use Automatic Matching')
908+ }
909+ _defaults = {
910+ 'period_id': _get_period,
911+ 'partner_id': _get_partner,
912+ 'journal_id':_get_journal,
913+ 'currency_id': _get_currency,
914+ 'reference': _get_reference,
915+ 'narration':_get_narration,
916+ 'type':_get_type,
917+ 'state': lambda *a: 'draft',
918+ 'pay_now':lambda *a: 'pay_later',
919+ 'name': lambda *a: '',
920+ 'date' : lambda *a: time.strftime('%Y-%m-%d'),
921+ 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.voucher',context=c),
922+ 'tax_id': _get_tax,
923+ }
924+
925+
926+ def compute_tax(self, cr, uid, ids, context={}):
927+ tax_pool = self.pool.get('account.tax')
928+ partner_pool = self.pool.get('res.partner')
929+ position_pool = self.pool.get('account.fiscal.position')
930+ voucher_line_pool = self.pool.get('account.voucher.line')
931+ voucher_pool = self.pool.get('account.voucher')
932+
933+ for voucher in voucher_pool.browse(cr, uid, ids, context):
934+ voucher_amount = 0.0
935+ for line in voucher.line_ids:
936+ voucher_amount += line.untax_amount or line.amount
937+ line.amount = line.untax_amount or line.amount
938+ voucher_line_pool.write(cr, uid, [line.id], {'amount':line.amount, 'untax_amount':line.untax_amount})
939+
940+ if not voucher.tax_id:
941+ self.write(cr, uid, [voucher.id], {'amount':voucher_amount, 'tax_amount':0.0})
942+ continue
943+
944+ tax = [tax_pool.browse(cr, uid, voucher.tax_id.id)]
945+ partner = partner_pool.browse(cr, uid, voucher.partner_id.id) or False
946+ taxes = position_pool.map_tax(cr, uid, partner and partner.property_account_position or False, tax)
947+ tax = tax_pool.browse(cr, uid, taxes)
948+
949+ total = voucher_amount
950+ total_tax = 0.0
951+
952+ if not tax[0].price_include:
953+ for tax_line in tax_pool.compute_all(cr, uid, tax, voucher_amount, 1).get('taxes',[]):
954+ total_tax += tax_line.get('amount')
955+ total += total_tax
956+ else:
957+ line_ids2 = []
958+ for line in voucher.line_ids:
959+ line_total = 0.0
960+ line_tax = 0.0
961+
962+ for tax_line in tax_pool.compute_all(cr, uid, tax, line.untax_amount or line.amount, 1).get('taxes',[]):
963+ line_tax += tax_line.get('amount')
964+ line_total += tax_line.get('price_unit')
965+ total_tax += line_tax
966+ untax_amount = line.untax_amount or line.amount
967+ voucher_line_pool.write(cr, uid, [line.id], {'amount':line_total, 'untax_amount':untax_amount})
968+
969+ self.write(cr, uid, [voucher.id], {'amount':total, 'tax_amount':total_tax})
970+ return True
971+
972+ def onchange_price(self, cr, uid, ids, line_ids, tax_id, partner_id=False, context={}):
973+ tax_pool = self.pool.get('account.tax')
974+ partner_pool = self.pool.get('res.partner')
975+ position_pool = self.pool.get('account.fiscal.position')
976+ voucher_line_pool = self.pool.get('account.voucher.line')
977+ res = {
978+ 'tax_amount':False,
979+ 'amount':False,
980+ }
981+ voucher_total_tax = 0.0
982+ voucher_total = 0.0
983+ voucher_line_ids = []
984+
985+ total = 0.0
986+ total_tax = 0.0
987+
988+ for line in line_ids:
989+ line_amount = 0.0
990+ line_amount = line[2].get('amount')
991+ voucher_line_ids += [line[1]]
992+ voucher_total += line_amount
993+
994+ total = voucher_total
995+ total_tax = 0.0
996+ if tax_id:
997+ tax = [tax_pool.browse(cr, uid, tax_id)]
998+ if partner_id:
999+ partner = partner_pool.browse(cr, uid, partner_id) or False
1000+ taxes = position_pool.map_tax(cr, uid, partner and partner.property_account_position or False, tax)
1001+ tax = tax_pool.browse(cr, uid, taxes)
1002+
1003+ if not tax[0].price_include:
1004+ for tax_line in tax_pool.compute_all(cr, uid, tax, voucher_total, 1).get('taxes',[]):
1005+ total_tax += tax_line.get('amount')
1006+ total += total_tax
1007+
1008+ res.update({
1009+ 'amount':total or voucher_total,
1010+ 'tax_amount':total_tax
1011+ })
1012+ return {
1013+ 'value':res
1014+ }
1015+
1016+ def onchange_term_id(self, cr, uid, ids, term_id, amount):
1017+ term_pool = self.pool.get('account.payment.term')
1018+ terms = False
1019+ due_date = False
1020+ default = {'date_due':False}
1021+ if term_id and amount:
1022+ terms = term_pool.compute(cr, uid, term_id, amount)
1023+ if terms:
1024+ due_date = terms[-1][0]
1025+ default.update({
1026+ 'date_due':due_date
1027+ })
1028+ return {'value':default}
1029+
1030+ def onchange_journal_voucher(self, cr, uid, ids, line_ids=False, tax_id=False, price=0.0, partner_id=False, journal_id=False, ttype=False, context={}):
1031+ """price
1032+ Returns a dict that contains new values and context
1033+
1034+ @param partner_id: latest value from user input for field partner_id
1035+ @param args: other arguments
1036+ @param context: context arguments, like lang, time zone
1037+
1038+ @return: Returns a dict which contains new values, and context
1039+ """
1040+ default = {
1041+ 'value':{},
1042+ }
1043+
1044+ if not partner_id or not journal_id:
1045+ return default
1046+
1047+ partner_pool = self.pool.get('res.partner')
1048+ journal_pool = self.pool.get('account.journal')
1049+
1050+ journal = journal_pool.browse(cr, uid, journal_id)
1051+ partner = partner_pool.browse(cr, uid, partner_id)
1052+ account_id = False
1053+ tr_type = False
1054+ if journal.type in ('sale','sale_refund'):
1055+ account_id = partner.property_account_receivable.id
1056+ tr_type = 'sale'
1057+ elif journal.type in ('purchase', 'purchase_refund','expense'):
1058+ account_id = partner.property_account_payable.id
1059+ tr_type = 'purchase'
1060+ else:
1061+ account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id
1062+ tr_type = 'receipt'
1063+
1064+ default['value']['account_id'] = account_id
1065+ default['value']['type'] = ttype or tr_type
1066+
1067+ vals = self.onchange_journal(cr, uid, ids, journal_id, line_ids, tax_id, partner_id, context)
1068+ default['value'].update(vals.get('value'))
1069+
1070+ return default
1071+
1072+ def onchange_partner_id(self, cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, context={}):
1073+ """price
1074+ Returns a dict that contains new values and context
1075+
1076+ @param partner_id: latest value from user input for field partner_id
1077+ @param args: other arguments
1078+ @param context: context arguments, like lang, time zone
1079+
1080+ @return: Returns a dict which contains new values, and context
1081+ """
1082+ currency_pool = self.pool.get('res.currency')
1083+ move_pool = self.pool.get('account.move')
1084+ line_pool = self.pool.get('account.voucher.line')
1085+ move_line_pool = self.pool.get('account.move.line')
1086+ partner_pool = self.pool.get('res.partner')
1087+ journal_pool = self.pool.get('account.journal')
1088+ default = {
1089+ 'value':{'line_ids':[], 'line_dr_ids':[], 'line_cr_ids':[], 'pre_line': False, 'currency_id':currency_id, 'journal_id': False},
1090+ }
1091+ if partner_id and not journal_id:
1092+ partner = partner_pool.browse(cr, uid, partner_id, context)
1093+ if partner._columns.has_key('payment_meth_id') and partner.payment_meth_id:
1094+ payment_mode_pool = self.pool.get('payment.mode')
1095+ payment_meth = payment_mode_pool.browse(cr, uid, partner.payment_meth_id.id, context)
1096+ if payment_meth:
1097+ default['value']['journal_id'] = payment_meth.journal.id
1098+ journal_id = payment_meth.journal.id
1099+ if not journal_id:
1100+ return {}
1101+
1102+ vals = self.onchange_journal(cr, uid, ids, journal_id, [], False, partner_id, context)
1103+ vals = vals.get('value')
1104+ currency_id = vals.get('currency_id', currency_id)
1105+
1106+ if not partner_id:
1107+ return default
1108+
1109+ if not partner_id and ids:
1110+ line_ids = line_pool.search(cr, uid, [('voucher_id','=',ids[0])])
1111+ if line_ids:
1112+ line_pool.unlink(cr, uid, line_ids)
1113+ return default
1114+
1115+ journal = journal_pool.browse(cr, uid, journal_id)
1116+ partner = partner_pool.browse(cr, uid, partner_id)
1117+ account_id = False
1118+ if journal.type in ('sale','sale_refund'):
1119+ account_id = partner.property_account_receivable.id
1120+ elif journal.type in ('purchase', 'purchase_refund','expense'):
1121+ account_id = partner.property_account_payable.id
1122+ elif journal.type in ('cash', 'bank'):
1123+ account_id = journal.default_debit_account_id.id
1124+ else:
1125+ account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id
1126+
1127+ if journal and not default['value']['currency_id']:
1128+ default['value']['currency_id'] = journal.company_id.currency_id.id
1129+ default['value']['account_id'] = account_id
1130+ if journal.type not in ('cash', 'bank'):
1131+ return default
1132+
1133+ total_credit = 0.0
1134+ total_debit = 0.0
1135+ account_type = 'receivable'
1136+ if ttype == 'payment':
1137+ account_type = 'payable'
1138+ total_debit = price or 0.0
1139+ else:
1140+ total_credit = price or 0.0
1141+ account_type = 'receivable'
1142+
1143+ if partner._columns.has_key('nat_acc_parent') and partner.nat_acc_parent:
1144+ partner_ids = partner_pool.search(cr,uid,[('parent_id','child_of',[partner_id])])
1145+ else:
1146+ partner_ids = [partner_id]
1147+ ids = move_line_pool.search(cr, uid, [('account_id.type','=', account_type), ('reconcile_id','=', False), ('partner_id','in',partner_ids)], context=context)
1148+ ids.reverse()
1149+ moves = move_line_pool.browse(cr, uid, ids)
1150+
1151+ company_currency = journal.company_id.currency_id.id
1152+ if company_currency != currency_id and ttype == 'payment':
1153+ total_debit = currency_pool.compute(cr, uid, currency_id, company_currency, total_debit)
1154+ elif company_currency != currency_id and ttype == 'receipt':
1155+ total_credit = currency_pool.compute(cr, uid, currency_id, company_currency, total_credit)
1156+
1157+ for line in moves:
1158+ if line.credit and line.reconcile_partial_id and ttype == 'receipt':
1159+ continue
1160+ if line.debit and line.reconcile_partial_id and ttype == 'payment':
1161+ continue
1162+ total_credit += line.credit or 0.0
1163+ total_debit += line.debit or 0.0
1164+
1165+ for line in moves:
1166+ if line.credit and line.reconcile_partial_id and ttype == 'receipt':
1167+ continue
1168+ if line.debit and line.reconcile_partial_id and ttype == 'payment':
1169+ continue
1170+
1171+ original_amount = line.credit or line.debit or 0.0
1172+ rs = {
1173+ 'name':line.move_id.name,
1174+ 'type': line.credit and 'dr' or 'cr',
1175+ 'move_line_id':line.id,
1176+ 'account_id':line.account_id.id,
1177+ 'amount_original':currency_pool.compute(cr, uid, company_currency, currency_id, original_amount),
1178+ 'date_original':line.date,
1179+ 'date_due':line.date_maturity,
1180+ 'amount_unreconciled':currency_pool.compute(cr, uid, company_currency, currency_id, line.amount_unreconciled)
1181+ }
1182+
1183+ def calc_amount(line, total):
1184+ return min(line.amount_unreconciled, total)
1185+
1186+ if line.credit:
1187+ amount = calc_amount(line, total_debit)
1188+ rs['amount'] = currency_pool.compute(cr, uid, company_currency, currency_id, amount)
1189+ total_debit -= amount
1190+ else:
1191+ amount = calc_amount(line, total_credit)
1192+ rs['amount'] = currency_pool.compute(cr, uid, company_currency, currency_id, amount)
1193+ total_credit -= amount
1194+
1195+ default['value']['line_ids'].append(rs)
1196+ if rs['type'] == 'cr':
1197+ default['value']['line_cr_ids'].append(rs)
1198+ else:
1199+ default['value']['line_dr_ids'].append(rs)
1200+
1201+ if ttype == 'payment' and len(default['value']['line_cr_ids']) > 0:
1202+ default['value']['pre_line'] = 1
1203+ elif ttype == 'receipt' and len(default['value']['line_dr_ids']) > 0:
1204+ default['value']['pre_line'] = 1
1205+ for credit_line in default['value']['line_dr_ids']:
1206+ credit_line['amount'] = 0.0
1207+ for invoice_line in default['value']['line_cr_ids']:
1208+ invoice_line['amount'] = 0.0
1209+ invoice_line['amount_difference'] = invoice_line['amount_unreconciled']
1210+
1211+ return default
1212+
1213+ def onchange_date(self, cr, user, ids, date, context={}):
1214+ """
1215+ @param date: latest value from user input for field date
1216+ @param args: other arguments
1217+ @param context: context arguments, like lang, time zone
1218+ @return: Returns a dict which contains new values, and context
1219+ """
1220+ period_pool = self.pool.get('account.period')
1221+ pids = period_pool.search(cr, user, [('date_start','<=',date), ('date_stop','>=',date)])
1222+ if not pids:
1223+ return {}
1224+ return {
1225+ 'value':{
1226+ 'period_id':pids[0]
1227+ }
1228+ }
1229+
1230+ def onchange_journal(self, cr, uid, ids, journal_id, line_ids, tax_id, partner_id, context={}):
1231+ if not journal_id:
1232+ return False
1233+ journal_pool = self.pool.get('account.journal')
1234+ journal = journal_pool.browse(cr, uid, journal_id)
1235+ account_id = journal.default_credit_account_id or journal.default_debit_account_id
1236+ tax_id = False
1237+ if account_id and account_id.tax_ids:
1238+ tax_id = account_id.tax_ids[0].id
1239+
1240+ vals = self.onchange_price(cr, uid, ids, line_ids, tax_id, partner_id, context)
1241+ vals['value'].update({'tax_id':tax_id})
1242+ currency_id = journal.company_id.currency_id.id
1243+ if journal.currency:
1244+ currency_id = journal.currency.id
1245+ vals['value'].update({'currency_id':currency_id})
1246+ return vals
1247+
1248+ def action_cancel_draft(self, cr, uid, ids, context={}):
1249+ wf_service = netsvc.LocalService("workflow")
1250+ for voucher_id in ids:
1251+ wf_service.trg_create(uid, 'account.voucher', voucher_id, cr)
1252+ self.write(cr, uid, ids, {'state':'draft'})
1253+ return True
1254+
1255+ def cancel_voucher(self, cr, uid, ids, context={}):
1256+ reconcile_pool = self.pool.get('account.move.reconcile')
1257+ move_pool = self.pool.get('account.move')
1258+ voucher_line_pool = self.pool.get('account.voucher.line')
1259+
1260+ for voucher in self.browse(cr, uid, ids):
1261+ recs = []
1262+ for line in voucher.move_ids:
1263+ if line.reconcile_id:
1264+ recs += [line.reconcile_id.id]
1265+ if line.reconcile_partial_id:
1266+ recs += [line.reconcile_partial_id.id]
1267+
1268+ reconcile_pool.unlink(cr, uid, recs)
1269+
1270+ if voucher.move_id:
1271+ move_pool.button_cancel(cr, uid, [voucher.move_id.id])
1272+ move_pool.unlink(cr, uid, [voucher.move_id.id])
1273+ res = {
1274+ 'state':'cancel',
1275+ 'move_id':False,
1276+ }
1277+ self.write(cr, uid, ids, res)
1278+ return True
1279+
1280+ def unlink(self, cr, uid, ids, context=None):
1281+ for t in self.read(cr, uid, ids, ['state'], context=context):
1282+ if t['state'] not in ('draft', 'cancel'):
1283+ raise osv.except_osv(_('Invalid action !'), _('Cannot delete Voucher(s) which are already opened or paid !'))
1284+ return super(account_voucher, self).unlink(cr, uid, ids, context=context)
1285+
1286+ # TODO: may be we can remove this method if not used anyware
1287+ def onchange_payment(self, cr, uid, ids, pay_now, journal_id, partner_id, ttype='sale'):
1288+ res = {}
1289+ if not partner_id:
1290+ return res
1291+ res = {'account_id':False}
1292+ partner_pool = self.pool.get('res.partner')
1293+ journal_pool = self.pool.get('account.journal')
1294+ if pay_now == 'pay_later':
1295+ partner = partner_pool.browse(cr, uid, partner_id)
1296+ journal = journal_pool.browse(cr, uid, journal_id)
1297+ if journal.type in ('sale','sale_refund'):
1298+ account_id = partner.property_account_receivable.id
1299+ elif journal.type in ('purchase', 'purchase_refund','expense'):
1300+ account_id = partner.property_account_payable.id
1301+ elif journal.type in ('cash', 'bank'):
1302+ account_id = journal.default_debit_account_id.id
1303+ else:
1304+ account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id
1305+ res['account_id'] = account_id
1306+ return {'value':res}
1307+
1308+ def calc_cash_discount(self, vch, line, amount=0):
1309+ # hook method for cash discount
1310+ return 0.0
1311+
1312+ def calc_interest(self, vch, line, amount=0):
1313+ # hook method for interest posting
1314+ return 0.0
1315+
1316+ def action_move_line_create(self, cr, uid, ids, context=None):
1317+
1318+ def _get_payment_term_lines(term_id, amount):
1319+ term_pool = self.pool.get('account.payment.term')
1320+ if term_id and amount:
1321+ terms = term_pool.compute(cr, uid, term_id, amount)
1322+ return terms
1323+ return False
1324+ if not context:
1325+ context = {}
1326+ move_pool = self.pool.get('account.move')
1327+ move_line_pool = self.pool.get('account.move.line')
1328+ analytic_pool = self.pool.get('account.analytic.line')
1329+ currency_pool = self.pool.get('res.currency')
1330+ invoice_pool = self.pool.get('account.invoice')
1331+ company_pool = self.pool.get('res.company')
1332+ for vch in self.browse(cr, uid, ids):
1333+ if vch.move_id:
1334+ continue
1335+
1336+ if 'force_name' in context and context['force_name']:
1337+ name = context['force_name']
1338+ elif vch.journal_id.sequence_id:
1339+ name = self.pool.get('ir.sequence').get_id(cr, uid, vch.journal_id.sequence_id.id)
1340+ else:
1341+ raise osv.except_osv(_('Error !'), _('Please define a sequence on the journal !'))
1342+
1343+ move = {
1344+ 'name' : name,
1345+ 'journal_id': vch.journal_id.id,
1346+ 'narration' : vch.narration,
1347+ 'date':vch.date,
1348+ 'ref':vch.reference,
1349+ 'period_id': vch.period_id and vch.period_id.id or False
1350+ }
1351+ move_id = move_pool.create(cr, uid, move)
1352+
1353+ #create the first line manually
1354+ company_currency = vch.journal_id.company_id.currency_id.id
1355+ debit = 0.0
1356+ credit = 0.0
1357+ # TODO: is there any other alternative then the voucher type ??
1358+ # -for sale, purchase we have but for the payment and receipt we do not have as based on the bank/cash journal we can not know its payment or receipt
1359+ if vch.type in ('purchase', 'payment'):
1360+ credit = currency_pool.compute(cr, uid, vch.currency_id.id, company_currency, vch.amount)
1361+ elif vch.type in ('sale', 'receipt'):
1362+ debit = currency_pool.compute(cr, uid, vch.currency_id.id, company_currency, vch.amount)
1363+ if debit < 0:
1364+ credit = -debit
1365+ debit = 0.0
1366+ if credit < 0:
1367+ debit = -credit
1368+ credit = 0.0
1369+
1370+ move_line = {
1371+ 'name':vch.name or '/',
1372+ 'debit':debit,
1373+ 'credit':credit,
1374+ 'account_id':vch.account_id.id,
1375+ 'move_id':move_id ,
1376+ 'journal_id':vch.journal_id.id,
1377+ 'period_id':vch.period_id.id,
1378+ 'partner_id':vch.partner_id.id,
1379+ 'currency_id':vch.currency_id.id,
1380+ 'amount_currency':vch.amount,
1381+ 'date':vch.date,
1382+ 'date_maturity':vch.date_due
1383+ }
1384+
1385+ if (debit == 0.0 or credit == 0.0 or debit+credit > 0) and (debit > 0.0 or credit > 0.0):
1386+ master_line = move_line_pool.create(cr, uid, move_line)
1387+ company = company_pool.browse(cr, uid, vch.company_id.id, context)
1388+ rec_list_ids = []
1389+ line_total = debit - credit
1390+ if vch.type == 'sale':
1391+ line_total = line_total - currency_pool.compute(cr, uid, vch.currency_id.id, company_currency, vch.tax_amount)
1392+ elif vch.type == 'purchase':
1393+ line_total = line_total + currency_pool.compute(cr, uid, vch.currency_id.id, company_currency, vch.tax_amount)
1394+
1395+ writeoff_ids = []
1396+ for line in vch.line_ids:
1397+ if not line.amount:
1398+ continue
1399+ amount_cash_discount = 0.0#self.calc_cash_discount(line, line.amount)
1400+ amount_writeoff = 0.0#calc_writeoff(line, amount_cash_discount)
1401+ amount_interest = 0.0#calc_interest(line, amount_cash_discount + amount_writeoff)
1402+ amount_currency = currency_pool.compute(cr, uid, vch.currency_id.id, company_currency, line.amount)
1403+ amount = amount_currency
1404+
1405+ move_line = {
1406+ 'journal_id':vch.journal_id.id,
1407+ 'period_id':vch.period_id.id,
1408+ 'name':line.name and line.name or '/',
1409+ 'account_id':line.account_id.id,
1410+ 'move_id':move_id,
1411+ 'partner_id':vch.partner_id.id,
1412+ 'currency_id':vch.currency_id.id,
1413+ 'amount_currency':amount,
1414+ 'analytic_account_id':line.account_analytic_id and line.account_analytic_id.id or False,
1415+ 'quantity':1,
1416+ 'credit':0.0,
1417+ 'debit':0.0,
1418+ 'date':vch.date
1419+ }
1420+ if line.invoice_id:
1421+ move_line['partner_id'] = line.invoice_id.partner_id.id
1422+ if amount < 0:
1423+ amount = -amount
1424+ if line.type == 'dr':
1425+ line.type = 'cr'
1426+ else:
1427+ line.type = 'dr'
1428+
1429+ if (line.type=='dr'):
1430+ line_total += amount
1431+ move_line['debit'] = amount
1432+ else:
1433+ line_total -= (amount+line.credit_used)
1434+ move_line['credit'] = (amount+line.credit_used)
1435+
1436+ if vch.tax_id and vch.type in ('sale', 'purchase'):
1437+ move_line.update({
1438+ 'account_tax_id':vch.tax_id.id,
1439+ })
1440+ master_line = move_line_pool.create(cr, uid, move_line)
1441+ if line.move_line_id.id:
1442+ rec_ids = [master_line, line.move_line_id.id]
1443+ rec_list_ids.append(rec_ids)
1444+ if not self.pool.get('res.currency').is_zero(cr, uid, vch.currency_id, line_total):
1445+ diff = line_total
1446+ move_line = {
1447+ 'name':name,
1448+ 'account_id':False,
1449+ 'move_id':move_id ,
1450+ 'partner_id':vch.partner_id.id,
1451+ 'date':vch.date,
1452+ 'credit':diff>0 and diff or 0.0,
1453+ 'debit':diff<0 and -diff or 0.0,
1454+ }
1455+ account_id = False
1456+ if vch.type in ('sale', 'receipt'):
1457+ account_id = vch.partner_id.property_account_receivable.id
1458+ else:
1459+ account_id = vch.partner_id.property_account_payable.id
1460+ move_line['account_id'] = account_id
1461+ move_line_id = move_line_pool.create(cr, uid, move_line)
1462+ if line.move_line_id.id:
1463+ rec_ids = [move_line, line.move_line_id.id]
1464+ rec_list_ids.append(rec_ids)
1465+
1466+ self.write(cr, uid, [vch.id], {
1467+ 'move_id': move_id,
1468+ 'state':'posted'
1469+ })
1470+ move_pool.post(cr, uid, [move_id], context={})
1471+ for rec_ids in rec_list_ids:
1472+ if len(rec_ids) >= 2:
1473+ move_line_pool.reconcile_partial(cr, uid, rec_ids)
1474+ return True
1475+
1476+ def copy(self, cr, uid, id, default={}, context=None):
1477+ default.update({
1478+ 'state':'draft',
1479+ 'number':False,
1480+ 'move_id':False,
1481+ 'line_cr_ids':False,
1482+ 'line_dr_ids':False,
1483+ 'reference':False
1484+ })
1485+ if 'date' not in default:
1486+ default['date'] = time.strftime('%Y-%m-%d')
1487+ return super(account_voucher, self).copy(cr, uid, id, default, context)
1488+
1489+
1490+account_voucher()
1491+
1492+class account_voucher_line(osv.osv):
1493+ _name = 'account.voucher.line'
1494+ _inherit = 'account.voucher.line'
1495+ _description = 'Voucher Lines'
1496+ _order = "date_due"
1497+
1498+ def write(self, cr, user, ids, vals, context=None):
1499+ '''
1500+ Add invoice and description in payment modification line
1501+ '''
1502+ if type(ids) == type([]):
1503+ move = self.browse(cr, user,ids[0]).move_line_id
1504+ else:
1505+ move = self.browse(cr, user,ids).move_line_id
1506+ if move:
1507+ vals['invoice_id'] = move.invoice and move.invoice.id
1508+ vals['name'] = move.invoice and move.invoice.number
1509+
1510+ return super(account_voucher_line, self).write(cr, user, ids, vals, context)
1511+
1512+ def create(self, cr, user, vals, context=None):
1513+ '''
1514+ Add invoice and description in payment modification line
1515+ '''
1516+ if vals.has_key('move_line_id') and vals['move_line_id']:
1517+ move = self.pool.get('account.move.line').browse(cr, user,vals['move_line_id'])
1518+ vals['invoice_id'] = move.invoice and move.invoice.id
1519+ vals['name'] = move.invoice and move.invoice.number
1520+ return super(account_voucher_line, self).create(cr, user, vals, context)
1521+
1522+ def calc_amt(self, cr, uid, ids, context={}):
1523+ '''
1524+ Function to calculate Pending Credits Used
1525+ '''
1526+ res = {}
1527+ result = 0.0
1528+
1529+ for id in ids:
1530+ res[id] = result
1531+ return res
1532+
1533+ def _credits_calc(self, cr, uid, ids, name, args, context):
1534+ credits_used_pool = self.pool.get('account.voucher.line.credits_to_use')
1535+ res={}
1536+ for line in self.browse(cr, uid, ids):
1537+ credits_used_ids = credits_used_pool.search(cr, uid, [('orginal_credit_line_id','=',line.id)])
1538+ res[line.id] = 0.0
1539+ if line.voucher_id.state != 'draft':
1540+ continue
1541+ for credit_used in credits_used_pool.browse(cr, uid, credits_used_ids, context=context):
1542+ if credit_used.use_credit:
1543+ res[line.id] += credit_used.discount_amount
1544+ return res
1545+ def _compute_credit_used(self, cr, uid, ids, name, args, context=None):
1546+ res = {}
1547+ for line in self.browse(cr, uid, ids):
1548+ res[line.id] = 0.0
1549+ for credit_line in line.available_credits:
1550+ if credit_line.use_credit :
1551+ res[line.id] += credit_line.discount_amount
1552+ return res
1553+
1554+ def _compute_balance(self, cr, uid, ids, name, args, context=None):
1555+ currency_pool = self.pool.get('res.currency')
1556+ rs_data = {}
1557+ for line in self.browse(cr, uid, ids):
1558+ res = {}
1559+ company_currency = line.voucher_id.journal_id.company_id.currency_id.id
1560+ voucher_currency = line.voucher_id.currency_id.id
1561+ move_line = line.move_line_id or False
1562+ if not move_line:
1563+ res['amount_original'] = 0.0
1564+ res['amount_unreconciled'] = 0.0
1565+
1566+ elif move_line and move_line.credit > 0:
1567+ res['amount_original'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, move_line.credit)
1568+ else:
1569+ res['amount_original'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, move_line.debit)
1570+
1571+ if move_line:
1572+ res['amount_unreconciled'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, move_line.amount_unreconciled - line.pending_credits )
1573+ if line.amount > 0.0:
1574+ res['amount_difference'] = res['amount_unreconciled'] - line.amount - line.credit_used
1575+ else:
1576+ res['amount_difference'] = 0.0
1577+ rs_data[line.id] = res
1578+ return rs_data
1579+
1580+ def _get_due_date(self, cr, uid, ids, context=None):
1581+ result = {}
1582+ for line in self.pool.get('account.move.line').browse(cr, uid, ids, context=context):
1583+# result[line.invoice_id.id] = True ##Changed by Jabir to fix error when clicking Post button from Customer Payment form. 2010/11/24
1584+ result[line.invoice.id] = True
1585+ return result.keys()
1586+ def _compute_discount_used(self, cr, uid, ids, name, args, context=None):
1587+ res = {}
1588+ for id in ids:
1589+ res[id] = 0.0
1590+ return res
1591+ def _calc_writeoff(self, cr, uid, ids, name, args, context):
1592+ res={}
1593+ for id in ids:
1594+ res[id] = 0.0
1595+ return res
1596+
1597+
1598+ def _order_compute_credit_used(self, cr, uid, ids, context=None):
1599+ """ Compute which all records to update
1600+ @return: List of ids
1601+ """
1602+ operation_ids = []
1603+ for credits_to_use in self.pool.get('account.voucher.line.credits_to_use').browse(cr,uid,ids,context=context):
1604+ operation_ids.append(credits_to_use.voucher_line_id and credits_to_use.voucher_line_id.id)
1605+ return operation_ids
1606+
1607+ def _get_voucher_line_ids(self, cr, uid, ids, context=None):
1608+ result = []
1609+ for vch_lines in self.pool.get('account.voucher').read(cr, uid, ids, ['line_ids'], context=context):
1610+ result +=vch_lines['line_ids']
1611+ return result
1612+ _columns = {
1613+ 'voucher_id':fields.many2one('account.voucher', 'Voucher', required=1, ondelete='cascade'),
1614+ 'invoice_id': fields.many2one('account.invoice', 'Invoice'),
1615+ 'name':fields.char('Description', size=256),
1616+ 'account_id':fields.many2one('account.account','G/L Account', required=True),
1617+ 'partner_id':fields.related('voucher_id', 'partner_id', type='many2one', relation='res.partner', string='Partner'),
1618+ 'untax_amount':fields.float('Untax Amount'),
1619+ 'amount':fields.float('Payment Amt', digits=(14,2), required=True),
1620+ 'type':fields.selection([('dr','Debit'),('cr','Credit')], 'Cr/Dr'),
1621+ 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'),
1622+ 'move_line_id': fields.many2one('account.move.line', 'Invoice'),
1623+ 'date_original': fields.related('move_line_id','date', type='date', relation='account.move.line', string='Invoice Date', readonly=True),
1624+ 'date_due': fields.related('move_line_id','date_maturity', type='date', relation='account.move.line', string='Due Date', readonly=True , store={
1625+ 'account.voucher.line': (lambda self, cr, uid, ids, c={}: ids, ['move_line_id'], 20),
1626+ 'account.move.line': (_get_due_date, ['date_maturity'], 20),
1627+ }),
1628+ 'amount_original': fields.function(_compute_balance, method=True, multi='dc', type='float', string='Original Amt', store=True, readonly=True),
1629+ 'amount_unreconciled': fields.function(_compute_balance, method=True, multi='dc', type='float', string='Amt Due', store=True, readonly=True),
1630+ 'company_id': fields.related('voucher_id','company_id',type='many2one', relation='res.company', string='Company', store={
1631+ 'account.voucher': (_get_voucher_line_ids, ['company_id'], 20),
1632+ }),
1633+ 'credit_used': fields.function(_compute_credit_used, method=True, type='float', string='Credit Used',
1634+ store={'account.voucher.line.credits_to_use':(_order_compute_credit_used,['use_credit','discount_amount'], 10)}, readonly=True),
1635+ 'amount_difference': fields.function(_compute_balance, method=True, multi='dc', type='float', string='Unpaid Amt', digits=(16, 2) ),
1636+ 'writeoff': fields.boolean("Writeoff"),
1637+ 'pay': fields.boolean("Pay", required=True),
1638+ 'pending_credits':fields.function(_credits_calc, method=True, string='Pending Credits Used', type='float'),
1639+ 'available_credits':fields.one2many('account.voucher.line.credits_to_use', 'voucher_line_id', 'Available credits' ),
1640+ 'discount_used': fields.function(_compute_discount_used, method=True, type='float', string='Discount Used', store=False, readonly=True),
1641+ 'writeoff_amount':fields.function(_calc_writeoff, method=True, string='Write-off Amt', type='float', store=False,),
1642+ 'discount_used': fields.function(_compute_discount_used, method=True, type='float', string='Discount Used', store=False, readonly=True),
1643+ }
1644+ _defaults = {
1645+ 'name': lambda *a: ''
1646+ }
1647+
1648+ def default_get(self, cr, user, fields_list, context=None):
1649+ """
1650+ Returns default values for fields
1651+ @param fields_list: list of fields, for which default values are required to be read
1652+ @param context: context arguments, like lang, time zone
1653+
1654+ @return: Returns a dict that contains default values for fields
1655+ """
1656+ journal_id = context.get('journal_id', False)
1657+ partner_id = context.get('partner_id', False)
1658+ journal_pool = self.pool.get('account.journal')
1659+ partner_pool = self.pool.get('res.partner')
1660+ values = super(account_voucher_line, self).default_get(cr, user, fields_list, context=context)
1661+ if (not journal_id) or ('account_id' not in fields_list):
1662+ return values
1663+ journal = journal_pool.browse(cr, user, journal_id)
1664+ account_id = False
1665+ ttype = 'cr'
1666+ if journal.type in ('sale', 'sale_refund'):
1667+ account_id = journal.default_credit_account_id and journal.default_credit_account_id.id or False
1668+ ttype = 'cr'
1669+ elif journal.type in ('purchase', 'expense', 'purchase_refund'):
1670+ account_id = journal.default_debit_account_id and journal.default_debit_account_id.id or False
1671+ ttype = 'dr'
1672+ elif partner_id:
1673+ partner = partner_pool.browse(cr, user, partner_id, context=context)
1674+ if context.get('type') == 'payment':
1675+ ttype = 'dr'
1676+ account_id = partner.property_account_payable.id
1677+ elif context.get('type') == 'receipt':
1678+ account_id = partner.property_account_receivable.id
1679+
1680+ if (not account_id) and 'account_id' in fields_list:
1681+ raise osv.except_osv(_('Invalid Error !'), _('Unable to locate account. Please change partner or payment method and try again !'))
1682+ values.update({
1683+ 'account_id':account_id,
1684+ 'type':ttype
1685+ })
1686+ return values
1687+
1688+ def recalculate_values(self, cr, uid, ids, context={}):
1689+ if type(ids) == type([]):
1690+ voucher_line = self.browse(cr,uid,ids[0])
1691+ else:
1692+ voucher_line = self.browse(cr,uid,ids)
1693+ self.pool.get('account.voucher').calc_diff(cr, uid, [voucher_line.voucher_id.id])
1694+ return True
1695+ def clear_values(self, cr, uid, ids, context={}):
1696+ '''
1697+ Clear all selected discounts, credits and writeoffs and manually entered values
1698+ '''
1699+ voucher_line = self.browse(cr,uid,ids[0])
1700+ if voucher_line._columns.has_key('writeoff_ids'):
1701+ if voucher_line._columns.has_key('available_discounts'):
1702+ for lines in self.read(cr,uid,ids,['available_credits','writeoff_ids','available_discounts']):
1703+ lines['writeoff_ids'] and self.pool.get('account.voucher.line.writeoff').unlink(cr,uid,lines['writeoff_ids'])
1704+ lines['available_credits'] and self.pool.get('account.voucher.line.credits_to_use').write(cr,uid,lines['available_credits'],
1705+ {'use_credit':False, 'discount_amount':0.0})
1706+ lines['available_discounts'] and self.pool.get('account.voucher.line.discount_to_use').write(cr,uid,lines['available_discounts'],
1707+ {'use_discount':False, 'discount_amount':0.0})
1708+ else:
1709+ for lines in self.read(cr,uid,ids,['available_credits','writeoff_ids']):
1710+ lines['writeoff_ids'] and self.pool.get('account.voucher.line.writeoff').unlink(cr,uid,lines['writeoff_ids'])
1711+ lines['available_credits'] and self.pool.get('account.voucher.line.credits_to_use').write(cr,uid,lines['available_credits'],
1712+ {'use_credit':False, 'discount_amount':0.0})
1713+ elif voucher_line._columns.has_key('available_discounts'):
1714+ for lines in self.read(cr,uid,ids,['available_credits','available_discounts']):
1715+ lines['available_credits'] and self.pool.get('account.voucher.line.credits_to_use').write(cr,uid,lines['available_credits'],
1716+ {'use_credit':False, 'discount_amount':0.0})
1717+ lines['available_discounts'] and self.pool.get('account.voucher.line.discount_to_use').write(cr,uid,lines['available_discounts'],
1718+ {'use_discount':False, 'discount_amount':0.0})
1719+ else:
1720+ for lines in self.read(cr,uid,ids,['available_credits']):
1721+ lines['available_credits'] and self.pool.get('account.voucher.line.credits_to_use').write(cr,uid,lines['available_credits'],
1722+ {'use_credit':False, 'discount_amount':0.0})
1723+ return True
1724+
1725+ def onchange_pay(self, cr, uid, ids, pay, amount_unreconciled,par_cr_ids, par_amount, credit_used, discount_used=0, writeoff_amount=0,context={}):
1726+ '''
1727+ Function to automativally fill the values when the pay checkbox is selected
1728+ '''
1729+ ret = {}
1730+ writeoff_amount = (not writeoff_amount and [0] or [writeoff_amount])[0]
1731+ discount_used = (not discount_used and [0] or [discount_used])[0]
1732+ credit_used = (not credit_used and [0] or [credit_used])[0]
1733+ if pay:
1734+ tot_amt = par_amount
1735+ for credit in par_cr_ids:
1736+ if credit[2]['pay']:
1737+ tot_amt -= (credit[2]['amount'])
1738+ if tot_amt < 0:
1739+ ret['amount'] = 0.0
1740+ else:
1741+ amount_unreconciled -= (discount_used+writeoff_amount+credit_used)
1742+ ret['amount'] = min(tot_amt,(amount_unreconciled<0) and 0 or amount_unreconciled)
1743+ else:
1744+ ret['amount'] = 0.0
1745+ return {'value':ret}
1746+
1747+
1748+account_voucher_line()
1749+
1750+class account_bank_statement(osv.osv):
1751+ _inherit = 'account.bank.statement'
1752+
1753+ def create_move_from_st_line(self, cr, uid, st_line_id, company_currency_id, next_number, context=None):
1754+ st_line = self.pool.get('account.bank.statement.line').browse(cr, uid, st_line_id, context=context)
1755+ if st_line.voucher_id:
1756+ res = self.pool.get('account.voucher').proforma_voucher(cr, uid, [st_line.voucher_id.id], context={'force_name': next_number})
1757+ return self.pool.get('account.move.line').write(cr, uid, [x.id for x in st_line.voucher_id.move_ids], {'statement_id': st_line.statement_id.id}, context=context)
1758+ return super(account_bank_statement, self).create_move_from_st_line(cr, uid, st_line, company_currency_id, next_number, context=context)
1759+account_bank_statement()
1760+
1761+class account_bank_statement_line(osv.osv):
1762+ _inherit = 'account.bank.statement.line'
1763+
1764+ def _amount_reconciled(self, cursor, user, ids, name, args, context=None):
1765+ '''
1766+ Function to calculate the value of variable Amount reconciled
1767+ '''
1768+ if not ids:
1769+ return {}
1770+ res_currency_obj = self.pool.get('res.currency')
1771+ res = {}
1772+ company_currency_id = False
1773+
1774+ for line in self.browse(cursor, user, ids, context=context):
1775+ if not company_currency_id:
1776+ company_currency_id = line.company_id.id
1777+ if line.voucher_id:
1778+ res[line.id] = res_currency_obj.compute(cursor, user,
1779+ company_currency_id, line.statement_id.currency.id,
1780+ line.voucher_id.amount, context=context)
1781+ else:
1782+ res[line.id] = 0.0
1783+ return res
1784+
1785+ _columns = {
1786+ 'amount_reconciled': fields.function(_amount_reconciled,
1787+ string='Amount reconciled', method=True, type='float'),
1788+ 'voucher_id': fields.many2one('account.voucher', 'Payment'),
1789+
1790+ }
1791+
1792+account_bank_statement_line()
1793+
1794+class account_voucher_line_credits_to_use(osv.osv):
1795+ _name = "account.voucher.line.credits_to_use"
1796+ _rec_name = 'inv_credit'
1797+
1798+ def _credit_balance(self, cr, uid, ids, name, args, context=None):
1799+ '''
1800+ Function to calculate the value of variable Credit Balance
1801+ '''
1802+ res = {}
1803+ for line in self.browse(cr, uid, ids, context=context):
1804+ if line.use_credit:
1805+ res[line.id] = line.available_amount - line.discount_amount
1806+ else:
1807+ res[line.id] = line.available_amount
1808+ return res
1809+
1810+ _columns = {
1811+ 'voucher_line_id': fields.many2one('account.voucher.line', 'Account Voucher line', ondelete='cascade', readonly=True),
1812+ 'orginal_credit_line_id': fields.many2one('account.voucher.line', 'Account Voucher Credit Line', ondelete='cascade', readonly=True),
1813+ 'use_credit': fields.boolean('Use Credit',help='Used to indicate if credit should used against the invoice.', required=True),
1814+ 'inv_credit': fields.many2one('account.move.line', 'Invoice', help='Invoice of the credit', readonly=True),
1815+ 'discount_window_date': fields.date('Date',help='Date of the original credit', readonly=True),
1816+ 'orginal_amount': fields.float('Original Credit Amt', readonly=True),
1817+ 'available_amount': fields.float('Credit Amt Available', readonly=True),
1818+ 'discount_amount': fields.float('Amt to Use',help='Enter the amount of discount to be given.', required=True),
1819+ 'gl_account' : fields.many2one('account.account', 'G/L Account',help='Enter the General Ledger account number to record taking the cash discount.', required=True,readonly=True),
1820+ 'credit_bal': fields.function(_credit_balance, string='Credit Balance', method=True, type='float', store=False),
1821+ }
1822+ def onchage_use_credit(self, cr, uid, ids, use_credit, available_amount, amount_difference, context=None):
1823+ '''
1824+ Function to automatically fill or remove the discount amount when use credit checkbox checked or unchecked
1825+ '''
1826+ res = {}
1827+ if use_credit:
1828+ res['value'] = {'discount_amount': min(available_amount, amount_difference)}
1829+ else:
1830+ res['value'] = {'discount_amount': 0}
1831+ return res
1832+ def onchage_discount_amount(self, cr, uid, ids, available_amount, discount_amount, context=None):
1833+ '''
1834+ Function to validate the discount amount
1835+ '''
1836+ res = {}
1837+ if discount_amount < 0:
1838+ res['value'] = {'discount_amount': 0, 'use_credit':False }
1839+ res['warning'] = {'title': 'Credit not in the limit', 'message': 'Credit should not be a negative value.'}
1840+ return res
1841+ if discount_amount > available_amount:
1842+ res['value'] = {'discount_amount': available_amount, 'use_credit':True }
1843+ res['warning'] = {'title': 'Credit not in the limit', 'message': 'Please adjust the Credit Amt value to be less than or equal the Credit Available.'}
1844+ return res
1845+ if discount_amount == 0.0:
1846+ res['value'] = { 'use_credit':False }
1847+ else:
1848+ res['value'] = { 'use_credit':True }
1849+ return res
1850+account_voucher_line_credits_to_use()
1851+
1852
1853=== added file 'account_voucher_credits_us/account_voucher_credits_us/voucher_payment_receipt_view.xml'
1854--- account_voucher_credits_us/account_voucher_credits_us/voucher_payment_receipt_view.xml 1970-01-01 00:00:00 +0000
1855+++ account_voucher_credits_us/account_voucher_credits_us/voucher_payment_receipt_view.xml 2011-10-06 15:24:58 +0000
1856@@ -0,0 +1,213 @@
1857+<?xml version="1.0" encoding="UTF-8"?>
1858+<openerp>
1859+ <data>
1860+ <record model="ir.ui.view" id="view_vendor_receipt_form_1">
1861+ <field name="name">account.voucher.receipt.form.1</field>
1862+ <field name="model">account.voucher</field>
1863+ <field name="type">form</field>
1864+ <field name="inherit_id" ref="account_voucher.view_vendor_receipt_form"/>
1865+ <field name="arch" type="xml">
1866+
1867+ <xpath expr="/form/group/field[@name='amount']" position="replace">
1868+ <field name="amount" string="Paid Amount" />
1869+ </xpath>
1870+ <xpath expr="/form/group[@col='10']/button[@name='cancel_voucher']" position="after">
1871+ <button name="calc_diff" string="Calculate" type="object" states="draft" icon="gtk-execute" default_focus="1" />
1872+ </xpath>
1873+ <xpath expr="/form/notebook/page[@string='Payment Information']/group[@colspan='1']/group[@colspan='1']/field[@name='number']" position="after">
1874+ <field name="auto_match" />
1875+ </xpath>
1876+ <xpath expr="/form/notebook/page[@string='Payment Information']/field[@name='line_dr_ids']/tree" position="replace">
1877+ <tree string="Credits" editable="bottom">
1878+ <field name="move_line_id"/>
1879+ <field name="date_original" readonly="1"/>
1880+ <field name="amount_original" readonly="1"/>
1881+ <field name="amount" sum="Total Amt Used" string="Amt Used" />
1882+ <field name="pending_credits" sum="Total Pending Credit Used" readonly="1"/>
1883+ <field name="amount_unreconciled" string="Amt Available" readonly="1"/>
1884+ <field name="account_id" groups="base.group_extended" domain="[('type','=','receivable')]"/>
1885+ </tree>
1886+ <form string="Credits">
1887+ <field name="move_line_id" />
1888+ <field name="date_original" readonly="1"/>
1889+ <field name="amount_original" readonly="1"/>
1890+ <field name="amount" string="Amount Used" readonly="False"/>
1891+ <field name="pending_credits" readonly="1" sum="Total Credit Used"/>
1892+ <field name="amount_unreconciled" string="Amount Available" readonly="1"/>
1893+ <field name="account_id" groups="base.group_extended" domain="[('type','=','receivable')]"/>
1894+ </form>
1895+ </xpath>
1896+
1897+ <xpath expr="/form/notebook/page[@string='Payment Information']/field[@name='line_cr_ids']" position="replace">
1898+ <field name="line_cr_ids" default_get="{'journal_id':journal_id, 'type':type, 'partner_id':partner_id}" colspan="4" nolabel="1" height="140" string="Payment Modification">
1899+ <tree string="Invoices and Outstanding Transactions" editable="bottom">
1900+ <field name="move_line_id" context="{'journal_id':parent.journal_id, 'partner_id':parent.partner_id}"
1901+ on_change="onchange_move_line_id(move_line_id)"
1902+ domain="[('account_id.type','in',('receivable','payable')), ('reconcile_id','=', False), ('partner_id','=',parent.partner_id)]"
1903+ />
1904+ <field name="date_original" readonly="1"/>
1905+ <field name="date_due" readonly="1"/>
1906+ <field name="amount_original" readonly="1"/>
1907+ <field name="amount_unreconciled" sum="Open Balance" readonly="1"/>
1908+ <field name="pay" on_change="onchange_pay(pay, amount_unreconciled, parent.line_cr_ids, parent.amount, credit_used, discount_used, writeoff_amount)"/>
1909+ <field name="amount" sum="Payment"/>
1910+ <field name="amount_difference"/>
1911+ <field name="credit_used"/>
1912+ <field name="account_id"/>
1913+ </tree>
1914+
1915+
1916+ <form >
1917+ <group string="Invoice" colspan="4" col="4" >
1918+ <group colspan="2" cols="2">
1919+ <field name="name" readonly="True"/>
1920+ <newline/>
1921+ <field name="invoice_id" readonly="True"/>
1922+ <newline/>
1923+ <field name="date_original" readonly="True"/>
1924+ </group>
1925+
1926+ <group color="red" colspan="2" cols="2" >
1927+ <field name="amount_original"/>
1928+ <newline/>
1929+ <field name="amount_unreconciled"/>
1930+ <newline/>
1931+ <field name='amount' />
1932+ <newline/>
1933+ <field name="credit_used"/>
1934+ <newline />
1935+ <field name="amount_difference"/>
1936+ <!--field name="discount_used"/>
1937+ <field name="tot_disc_amt"/>
1938+ <newline/>
1939+ <field name="tot_cred_amt"/>
1940+ <newline/>
1941+ <field name="tot_dis_cred_amt"/>
1942+ <newline/>
1943+ <field name="rem_due"/-->
1944+ </group>
1945+ </group>
1946+ <notebook tabpos="up" colspan="4" >
1947+ <page string="Credit">
1948+ <field name="available_credits" nolabel="1" colspan="4" string="Avilable Credits" view_mode="tree" >
1949+ </field>
1950+ </page>
1951+ <page string="Other Info" readonly="1">
1952+ <field name='account_id'/>
1953+ <field name='partner_id' readonly="1"/>
1954+ <field name='untax_amount' readonly="1"/>
1955+ <field name='type' readonly="1"/>
1956+ <field name='account_analytic_id' readonly="1"/>
1957+ <field name='date_due' readonly="1"/>
1958+ <field name='company_id' readonly="1"/>
1959+ <field name='pay' on_change="onchange_pay(pay, amount_unreconciled, parent.line_cr_ids, parent.amount, credit_used, discount_used, writeoff_amount)"/>
1960+ </page>
1961+ </notebook>
1962+ <group colspan="1" cols="1">
1963+ <label/><label/>
1964+ <button name="clear_values" icon='gtk-clear' string="Clear" type="object" colspan="1"/>
1965+ <button name="recalculate_values" icon='gtk-refresh' string="Re-Calculate" type="object" colspan="1"/>
1966+ </group>
1967+ </form>
1968+
1969+ </field>
1970+ </xpath>
1971+
1972+ </field>
1973+ </record>
1974+
1975+
1976+ <record model="ir.ui.view" id="view_account_voucher_line_credits_to_use_tree">
1977+ <field name="name">account.voucher.line.credits_to_use.tree</field>
1978+ <field name="model">account.voucher.line.credits_to_use</field>
1979+ <field name="type">tree</field>
1980+ <field name="arch" type="xml">
1981+ <tree string="Available Credits" editable="top" >
1982+ <field name="use_credit" width="100" on_change="onchage_use_credit(use_credit, available_amount, parent.amount_difference)"/>
1983+ <field name="inv_credit"/>
1984+ <field name="discount_window_date"/>
1985+ <field name="orginal_amount"/>
1986+ <field name="available_amount"/>
1987+ <field name="discount_amount" on_change="onchage_discount_amount(available_amount, discount_amount)"/>
1988+ <field name="credit_bal"/>
1989+ <field name="gl_account"/>
1990+ </tree>
1991+ </field>
1992+ </record>
1993+
1994+ <record model="ir.ui.view" id="view_vendor_receipt_form_2">
1995+ <field name="name">account.voucher.receipt.form.2</field>
1996+ <field name="model">account.voucher</field>
1997+ <field name="type">form</field>
1998+ <field name="inherit_id" ref="account_voucher.view_vendor_receipt_form"/>
1999+ <field name="arch" type="xml">
2000+ <xpath expr="/form/notebook/page[@string='Payment Information']/field/tree/field[@name='move_line_id']" position="replace">
2001+ <field name="move_line_id" context="{'journal_id':parent.journal_id, 'partner_id':parent.partner_id}"
2002+ domain="[('account_id.type','in',('receivable','payable')), ('reconcile_id','=', False), ('partner_id','=',parent.partner_id)]"
2003+ />
2004+ </xpath>
2005+ </field>
2006+ </record>
2007+ <record model="ir.ui.view" id="view_vendor_receipt_form_3">
2008+ <field name="name">account.voucher.receipt.form.3</field>
2009+ <field name="model">account.voucher</field>
2010+ <field name="type">form</field>
2011+ <field name="inherit_id" ref="account_voucher.view_vendor_receipt_form"/>
2012+ <field name="arch" type="xml">
2013+ <xpath expr="/form/notebook/page[@string='Payment Information']/field/tree/field[@name='account_id']" position="replace">
2014+ <field name="account_id" groups="base.group_extended" domain="[('type','=','receivable')]"/>
2015+ </xpath>
2016+ </field>
2017+ </record>
2018+ <record model="ir.ui.view" id="view_vendor_receipt_form_4">
2019+ <field name="name">account.voucher.receipt.form.4</field>
2020+ <field name="model">account.voucher</field>
2021+ <field name="type">form</field>
2022+ <field name="inherit_id" ref="account_voucher.view_vendor_receipt_form"/>
2023+ <field name="arch" type="xml">
2024+ <xpath expr="/form/notebook/page[@string='Payment Information']/field/tree/field[@name='amount']" position="replace">
2025+ <field name="amount" sum="Payment" />
2026+ </xpath>
2027+ </field>
2028+ </record>
2029+
2030+ <record id="action_proforma_voucher" model="ir.actions.act_window">
2031+ <field name="name">Post Voucher</field>
2032+ <field name="type">ir.actions.act_window</field>
2033+ <field name="res_model">account.post.voucher</field>
2034+ <field name="view_type">form</field>
2035+ <field name="view_mode">form</field>
2036+ <field name="target">new</field>
2037+ </record>
2038+ <record model="ir.ui.view" id="view_vendor_receipt_form_5">
2039+ <field name="name">account.voucher.receipt.form.5</field>
2040+ <field name="model">account.voucher</field>
2041+ <field name="type">form</field>
2042+ <field name="inherit_id" ref="account_voucher.view_vendor_receipt_form"/>
2043+ <field name="arch" type="xml">
2044+ <xpath expr="/form/group[@col='10']/button[@name='proforma_voucher']" position="replace">
2045+ <button name="%(action_proforma_voucher)d" string="Post" states="draft" type="action" icon="terp-camera_test"/>
2046+ </xpath>
2047+ </field>
2048+ </record>
2049+ <!--
2050+ Company
2051+ -->
2052+ <record id="view_company_form_jdc3" model="ir.ui.view">
2053+ <field name="name">res.company.form.jdc3</field>
2054+ <field name="model">res.company</field>
2055+ <field name="type">form</field>
2056+ <field name="inherit_id" ref="base.view_company_form"/>
2057+ <field name="priority" eval="1"/>
2058+ <field name="arch" type="xml">
2059+ <xpath expr="/form/notebook" position="inside">
2060+ <page string="Accounting">
2061+ <field name="writeoff_account" colspan="2"/>
2062+ </page>
2063+ </xpath>
2064+ </field>
2065+ </record>
2066+
2067+
2068+ </data>
2069+</openerp>
2070
2071=== added directory 'account_voucher_credits_us/account_voucher_credits_us/wizard'
2072=== added file 'account_voucher_credits_us/account_voucher_credits_us/wizard/__init__.py'
2073--- account_voucher_credits_us/account_voucher_credits_us/wizard/__init__.py 1970-01-01 00:00:00 +0000
2074+++ account_voucher_credits_us/account_voucher_credits_us/wizard/__init__.py 2011-10-06 15:24:58 +0000
2075@@ -0,0 +1,25 @@
2076+# -*- encoding: utf-8 -*-
2077+##############################################################################
2078+#
2079+# OpenERP, Open Source Management Solution
2080+# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
2081+#
2082+# This program is free software: you can redistribute it and/or modify
2083+# it under the terms of the GNU Affero General Public License as
2084+# published by the Free Software Foundation, either version 3 of the
2085+# License, or (at your option) any later version.
2086+#
2087+# This program is distributed in the hope that it will be useful,
2088+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2089+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2090+# GNU Affero General Public License for more details.
2091+#
2092+# You should have received a copy of the GNU Affero General Public License
2093+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2094+#
2095+##############################################################################
2096+
2097+import account_post_voucher
2098+
2099+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
2100+
2101
2102=== added file 'account_voucher_credits_us/account_voucher_credits_us/wizard/account_post_voucher.py'
2103--- account_voucher_credits_us/account_voucher_credits_us/wizard/account_post_voucher.py 1970-01-01 00:00:00 +0000
2104+++ account_voucher_credits_us/account_voucher_credits_us/wizard/account_post_voucher.py 2011-10-06 15:24:58 +0000
2105@@ -0,0 +1,73 @@
2106+# -*- coding: utf-8 -*-
2107+##############################################################################
2108+#
2109+# OpenERP, Open Source Management Solution
2110+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
2111+#
2112+# This program is free software: you can redistribute it and/or modify
2113+# it under the terms of the GNU Affero General Public License as
2114+# published by the Free Software Foundation, either version 3 of the
2115+# License, or (at your option) any later version.
2116+#
2117+# This program is distributed in the hope that it will be useful,
2118+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2119+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2120+# GNU Affero General Public License for more details.
2121+#
2122+# You should have received a copy of the GNU Affero General Public License
2123+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2124+#
2125+##############################################################################
2126+from osv import fields, osv
2127+
2128+class account_post_voucher(osv.osv_memory):
2129+ _name = 'account.post.voucher'
2130+ _description = 'Account Pay Voucher'
2131+
2132+ _columns = {
2133+ 'total_paid': fields.float('Total Received'),
2134+ 'total_allocated': fields.float('Total Allocated'),
2135+ 'ok_to_go': fields.float('OK to Go'),
2136+ }
2137+
2138+ def _get_total_paid(self, cr, uid, context={}):
2139+ obj_voucher = self.pool.get('account.voucher')
2140+ return obj_voucher.browse(cr, uid, context['active_id'], context).amount
2141+
2142+ def _get_total_allocated(self, cr, uid, context={}):
2143+ obj_voucher = self.pool.get('account.voucher')
2144+ voucher = obj_voucher.browse(cr, uid, context['active_id'], context)
2145+ total_allocated = 0.0
2146+ for line in voucher.line_cr_ids:
2147+ total_allocated += line.amount
2148+ return total_allocated
2149+
2150+ def _get_ok_to_go(self,cr, uid, context={}):
2151+ obj_voucher = self.pool.get('account.voucher')
2152+ voucher = obj_voucher.browse(cr, uid, context['active_id'], context)
2153+ total_allocated = 0.0
2154+ for line in voucher.line_cr_ids:
2155+ total_allocated += line.amount
2156+ return total_allocated - voucher.amount
2157+
2158+ _defaults = {
2159+ 'total_paid': _get_total_paid,
2160+ 'total_allocated': _get_total_allocated,
2161+ 'ok_to_go': _get_ok_to_go,
2162+ }
2163+ def onchange_ok_to_go(self,cr, uid, ids, ok_to_go, context={}):
2164+ if ok_to_go > 0.0:
2165+ return {'warning': {'title': 'Overallocated invoices', 'message': 'Reduce allocations to match Total Receipt'}}
2166+ else:
2167+ return {}
2168+ def launch_wizard(self, cr, uid, ids, context=None):
2169+ """
2170+ Don't allow post if total_allocated > total_paid.
2171+ """
2172+ obj_voucher = self.pool.get('account.voucher')
2173+ obj_voucher.action_move_line_create(cr, uid, context['active_ids'], context)
2174+ return {}
2175+
2176+account_post_voucher()
2177+
2178+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
2179
2180=== added file 'account_voucher_credits_us/account_voucher_credits_us/wizard/account_post_voucher.xml'
2181--- account_voucher_credits_us/account_voucher_credits_us/wizard/account_post_voucher.xml 1970-01-01 00:00:00 +0000
2182+++ account_voucher_credits_us/account_voucher_credits_us/wizard/account_post_voucher.xml 2011-10-06 15:24:58 +0000
2183@@ -0,0 +1,40 @@
2184+<?xml version="1.0" encoding="utf-8"?>
2185+<openerp>
2186+ <data>
2187+
2188+ <record id="account_voucher_post_view" model="ir.ui.view">
2189+ <field name="name">account.post.voucher.form</field>
2190+ <field name="model">account.post.voucher</field>
2191+ <field name="type">form</field>
2192+ <field name="arch" type="xml">
2193+ <form string="Post Voucher">
2194+ <group colspan="2" col="4">
2195+ <field name="total_paid" readonly="True"/>
2196+ <field name="total_allocated" readonly="True"/>
2197+ <field name="ok_to_go" invisible="True" on_change="onchange_ok_to_go(ok_to_go)"/>
2198+ </group>
2199+ <group height="100" width="320">
2200+ <separator string="Confirm Amounts?"/>
2201+ <newline/>
2202+ <group colspan="2" col="4">
2203+ <button special="cancel" string="Cancel" icon="gtk-cancel"/>
2204+ <button name="launch_wizard" string="Yes" type="object" attrs="{'invisible':[('ok_to_go', '>', 0.0)]}" icon="gtk-ok" default_focus="1"/>
2205+ </group>
2206+ </group>
2207+ </form>
2208+ </field>
2209+ </record>
2210+
2211+ <record id="action_proforma_voucher" model="ir.actions.act_window">
2212+ <field name="name">Post Voucher</field>
2213+ <field name="type">ir.actions.act_window</field>
2214+ <field name="res_model">account.post.voucher</field>
2215+ <field name="view_type">form</field>
2216+ <field name="view_mode">form</field>
2217+ <field name="view_id" ref="account_voucher_post_view"/>
2218+ <field name="target">new</field>
2219+ </record>
2220+
2221+
2222+ </data>
2223+</openerp>
2224
2225=== added directory 'account_voucher_credits_us/security'
2226=== added file 'account_voucher_credits_us/security/ir.model.access.csv'
2227--- account_voucher_credits_us/security/ir.model.access.csv 1970-01-01 00:00:00 +0000
2228+++ account_voucher_credits_us/security/ir.model.access.csv 2011-10-06 15:24:58 +0000
2229@@ -0,0 +1,2 @@
2230+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
2231+"access_account_voucher_line_credits_to_use","account.voucher.line.credits_to_use","model_account_voucher_line_credits_to_use","base.group_user",1,1,1,1
2232\ No newline at end of file
2233
2234=== added directory 'account_voucher_credits_us/test'
2235=== added file 'account_voucher_credits_us/test/account_voucher.yml'
2236--- account_voucher_credits_us/test/account_voucher.yml 1970-01-01 00:00:00 +0000
2237+++ account_voucher_credits_us/test/account_voucher.yml 2011-10-06 15:24:58 +0000
2238@@ -0,0 +1,97 @@
2239+
2240+-
2241+ In order to check account voucher module in OpenERP I create a customer voucher
2242+-
2243+ !record {model: account.voucher, id: account_voucher_voucherforaxelor0}:
2244+ account_id: account.cash
2245+ company_id: base.main_company
2246+ currency_id: base.EUR
2247+ journal_id: account.bank_journal
2248+ name: Voucher for Axelor
2249+ narration: Basic Pc
2250+ line_ids:
2251+ - account_id: account.a_recv
2252+ amount: 1000.0
2253+ name: Voucher for Axelor
2254+ partner_id: base.res_partner_desertic_hispafuentes
2255+ period_id: account.period_6
2256+ reference_type: none
2257+
2258+-
2259+ I check that Initially customer voucher is in the "Draft" state
2260+-
2261+ !assert {model: account.voucher, id: account_voucher_voucherforaxelor0}:
2262+ - state == 'draft'
2263+-
2264+ I compute the voucher to calculate the taxes by clicking Cpmpute button
2265+-
2266+ !workflow {model: account.voucher, action: proforma_voucher, ref: account_voucher_voucherforaxelor0}
2267+-
2268+ I check that the voucher state is now "proforma"
2269+-
2270+ !assert {model: account.voucher, id: account_voucher_voucherforaxelor0}:
2271+ - state == 'proforma'
2272+-
2273+ I create voucher by clicking on Create button
2274+-
2275+ !workflow {model: account.voucher, action: proforma_voucher, ref: account_voucher_voucherforaxelor0}
2276+-
2277+ I clicked on Validate Button
2278+-
2279+ !assert {model: account.voucher, id: account_voucher_voucherforaxelor0}:
2280+ - state == 'posted'
2281+
2282+-
2283+ I check that Moves get created for this voucher
2284+-
2285+ !python {model: account.voucher}: |
2286+ acc_id=self.browse(cr, uid, ref("account_voucher_voucherforaxelor0"))
2287+ assert(acc_id.move_id)
2288+
2289+
2290+-
2291+ Now I create a Vendor Voucher
2292+-
2293+ !record {model: account.voucher, id: account_voucher_voucheraxelor0}:
2294+ account_id: account.cash
2295+ company_id: base.main_company
2296+ currency_id: base.EUR
2297+ journal_id: account.bank_journal
2298+ name: Voucher Axelor
2299+ narration: Basic PC
2300+ line_ids:
2301+ - account_id: account.cash
2302+ amount: 1000.0
2303+ name: Voucher Axelor
2304+ period_id: account.period_6
2305+ reference_type: none
2306+
2307+-
2308+ I check that Initially vendor voucher is in the "Draft" state
2309+-
2310+ !assert {model: account.voucher, id: account_voucher_voucheraxelor0}:
2311+ - state == 'draft'
2312+-
2313+ I change the state of voucher to "proforma" by clicking PRO-FORMA button
2314+-
2315+ !workflow {model: account.voucher, action: proforma_voucher, ref: account_voucher_voucheraxelor0}
2316+-
2317+ I check that the voucher state is now "proforma"
2318+-
2319+ !assert {model: account.voucher, id: account_voucher_voucheraxelor0}:
2320+ - state == 'proforma'
2321+-
2322+ I create voucher by clicking on Create button
2323+-
2324+ !workflow {model: account.voucher, action: proforma_voucher, ref: account_voucher_voucheraxelor0}
2325+-
2326+ I check that the voucher state is "posted"
2327+-
2328+ !assert {model: account.voucher, id: account_voucher_voucheraxelor0}:
2329+ - state == 'posted'
2330+-
2331+ I check that moves get created for this voucher
2332+-
2333+ !python {model: account.voucher}: |
2334+ acc_id=self.browse(cr, uid, ref("account_voucher_voucheraxelor0"))
2335+ assert(acc_id.move_id)
2336
2337=== added file 'account_voucher_credits_us/test/account_voucher_report.yml'
2338--- account_voucher_credits_us/test/account_voucher_report.yml 1970-01-01 00:00:00 +0000
2339+++ account_voucher_credits_us/test/account_voucher_report.yml 2011-10-06 15:24:58 +0000
2340@@ -0,0 +1,25 @@
2341+-
2342+ Demo for Account Voucher
2343+-
2344+ !record {model: account.voucher, id: account_voucher_voucheraxelor0}:
2345+ account_id: account.cash
2346+ company_id: base.main_company
2347+ currency_id: base.EUR
2348+ journal_id: account.bank_journal
2349+ name: Voucher Axelor
2350+ narration: Basic PC
2351+ amount: 1000.0
2352+ line_ids:
2353+ - account_id: account.cash
2354+ amount: 1000.0
2355+ name: Voucher Axelor
2356+ period_id: account.period_6
2357+
2358+-
2359+ In order to test the PDF reports defined on a account_voucher, we will print account voucher Report
2360+-
2361+ !python {model: account.voucher}: |
2362+ import netsvc, tools, os
2363+ (data, format) = netsvc.LocalService('report.voucher.cash_receipt.drcr').create(cr, uid, [ref("account_voucher_voucheraxelor0")], {}, {})
2364+ if tools.config['test_report_directory']:
2365+ file(os.path.join(tools.config['test_report_directory'], 'account_voucher-report.'+format), 'wb+').write(data)
2366
2367=== added file 'account_voucher_credits_us/test/sales_payment.yml'
2368--- account_voucher_credits_us/test/sales_payment.yml 1970-01-01 00:00:00 +0000
2369+++ account_voucher_credits_us/test/sales_payment.yml 2011-10-06 15:24:58 +0000
2370@@ -0,0 +1,89 @@
2371+-
2372+ In order to test account voucher i will create an invoice and pay it through account voucher.
2373+-
2374+ I create a new Partner
2375+-
2376+ !record {model: res.partner, id: res_partner_micropc0}:
2377+ address:
2378+ - country_id: base.be
2379+ name: Jenifer
2380+ street: 69 rue de Chimay
2381+ type: default
2382+ zip: '5478'
2383+ category_id:
2384+ - base.res_partner_category_8
2385+ credit_limit: 0.0
2386+ name: Micro PC
2387+ property_account_payable: account.a_pay
2388+ property_account_receivable: account.a_recv
2389+
2390+-
2391+ Create an invoice for the partner
2392+-
2393+ !record {model: account.invoice, id: account_invoice_0}:
2394+ account_id: account.a_recv
2395+ address_contact_id: base.res_partner_address_7
2396+ address_invoice_id: base.res_partner_address_7
2397+ company_id: base.main_company
2398+ currency_id: base.EUR
2399+ invoice_line:
2400+ - account_id: account.a_sale
2401+ name: '[PC1] Basic PC'
2402+ price_unit: 450.0
2403+ quantity: 1.0
2404+ product_id: product.product_product_pc1
2405+ uos_id: product.product_uom_unit
2406+ journal_id: account.sales_journal
2407+ partner_id: res_partner_micropc0
2408+
2409+-
2410+ I check that the customer invoice is in draft state
2411+-
2412+ !assert {model: account.invoice, id: account_invoice_0}:
2413+ - state == 'draft'
2414+
2415+-
2416+ I make the invoice in Open state
2417+-
2418+ !workflow {model: account.invoice, action: invoice_open, ref: account_invoice_0}
2419+
2420+-
2421+ I check that a payment entry gets created in the account.move.line
2422+-
2423+ !python {model: account.invoice}: |
2424+ acc_id=self.browse(cr, uid, ref("account_invoice_0"))
2425+ assert(acc_id.move_id)
2426+
2427+-
2428+ I will create and post an account voucher for the partner.
2429+-
2430+ !python {model: account.voucher}: |
2431+ import netsvc
2432+ vals = {}
2433+ journal_id = self.default_get(cr, uid, ['journal_id']).get('journal_id',None)
2434+ res = self.onchange_partner_id(cr, uid, [], ref("res_partner_micropc0"), journal_id, price=0.0, ttype='receipt')
2435+ vals = {
2436+ 'account_id': ref('account.cash'),
2437+ 'amount': 450.0,
2438+ 'company_id': ref('base.main_company'),
2439+ 'currency_id': ref('base.EUR'),
2440+ 'journal_id': ref('account.bank_journal'),
2441+ 'partner_id': ref('res_partner_micropc0'),
2442+ 'period_id': ref('account.period_8'),
2443+ 'type': 'receipt',
2444+ }
2445+ if not res['value']['line_cr_ids']:
2446+ res['value']['line_cr_ids'] = [{'type': 'cr', 'account_id': ref('account.a_recv'),}]
2447+ res['value']['line_cr_ids'][0]['amount'] = 450.0
2448+ vals['line_cr_ids'] = [(0,0,i) for i in res['value']['line_cr_ids']]
2449+ id = self.create(cr, uid, vals)
2450+ voucher_id = self.browse(cr, uid, id)
2451+ assert (voucher_id.state=='draft'), "Voucher is not in draft state"
2452+ wf_service = netsvc.LocalService("workflow")
2453+ wf_service.trg_validate(uid, 'account.voucher', voucher_id.id, 'proforma_voucher', cr)
2454+
2455+-
2456+ Finally i will Confirm the state of the invoice is paid
2457+-
2458+ !assert {model: account.invoice, id: account_invoice_0}:
2459+ - state == 'paid'
2460
2461=== added file 'account_voucher_credits_us/test/sales_receipt.yml'
2462--- account_voucher_credits_us/test/sales_receipt.yml 1970-01-01 00:00:00 +0000
2463+++ account_voucher_credits_us/test/sales_receipt.yml 2011-10-06 15:24:58 +0000
2464@@ -0,0 +1,74 @@
2465+
2466+-
2467+ In order to test sales receipt i will create a sale receipt and pay it through sales payment
2468+-
2469+ First of all I create a voucher
2470+-
2471+ !record {model: account.voucher, id: account_voucher_chinaexport_0}:
2472+ account_id: account.a_recv
2473+ amount: 30000.0
2474+ company_id: base.main_company
2475+ journal_id: account.sales_journal
2476+ line_cr_ids:
2477+ - account_id: account.a_sale
2478+ amount: 30000.0
2479+ partner_id: base.res_partner_3
2480+ period_id: account.period_8
2481+ tax_amount: 0.0
2482+ type: sale
2483+
2484+-
2485+ I check that the voucher state is Draft
2486+-
2487+ !assert {model: account.voucher, id: account_voucher_chinaexport_0}:
2488+ - state == 'draft'
2489+
2490+-
2491+ I clicked on post button to post the voucher
2492+-
2493+ !workflow {model: account.voucher, action: proforma_voucher, ref: account_voucher_chinaexport_0}
2494+
2495+-
2496+ Check the voucher state is Posted
2497+-
2498+ !assert {model: account.voucher, id: account_voucher_chinaexport_0}:
2499+ - state == 'posted'
2500+
2501+-
2502+ I create a voucher record for the same partner
2503+-
2504+ !record {model: account.voucher, id: account_voucher_chinaexport_1}:
2505+ account_id: account.cash
2506+ amount: 30000.0
2507+ company_id: base.main_company
2508+ currency_id: base.EUR
2509+ journal_id: account.bank_journal
2510+ line_cr_ids:
2511+ - account_id: account.a_recv
2512+ amount: 0.0
2513+ name: 2010/003
2514+ type: cr
2515+ - account_id: account.a_recv
2516+ amount: 30000.0
2517+ name: 2010/003
2518+ type: cr
2519+ partner_id: base.res_partner_3
2520+ period_id: account.period_8
2521+ type: receipt
2522+
2523+-
2524+ Check the voucher state is draft
2525+-
2526+ !assert {model: account.voucher, id: account_voucher_chinaexport_1}:
2527+ - state == 'draft'
2528+
2529+-
2530+ I clicked on Post button to post the voucher
2531+-
2532+ !workflow {model: account.voucher, action: proforma_voucher, ref: account_voucher_chinaexport_1}
2533+
2534+-
2535+ Check the voucher state is Posted
2536+-
2537+ !assert {model: account.voucher, id: account_voucher_chinaexport_1}:
2538+ - state == 'posted'
2539
2540=== added file 'account_voucher_credits_us/voucher.py'
2541--- account_voucher_credits_us/voucher.py 1970-01-01 00:00:00 +0000
2542+++ account_voucher_credits_us/voucher.py 2011-10-06 15:24:58 +0000
2543@@ -0,0 +1,1100 @@
2544+# -*- coding: utf-8 -*-
2545+##############################################################################
2546+#
2547+# OpenERP, Open Source Management Solution
2548+# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
2549+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
2550+#
2551+# This program is free software: you can redistribute it and/or modify
2552+# it under the terms of the GNU General Public License as published by
2553+# the Free Software Foundation, either version 3 of the License, or
2554+# (at your option) any later version.
2555+#
2556+# This program is distributed in the hope that it will be useful,
2557+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2558+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2559+# GNU General Public License for more details.
2560+#
2561+# You should have received a copy of the GNU General Public License
2562+# along with this program. If not, see <http://www.gnu.org/licenses/>
2563+#
2564+##############################################################################
2565+
2566+import time
2567+import netsvc
2568+from osv import fields
2569+from osv import osv
2570+from tools.translate import _
2571+def _combinations(iterable, r):
2572+ '''
2573+ @return: combination generator object
2574+
2575+ Example
2576+ combinations(’ABCD’, 2) --> AB AC AD BC BD CD
2577+ combinations(range(4), 3) --> 012 013 023 123
2578+ '''
2579+ pool = tuple(iterable)
2580+ n = len(pool)
2581+ if r > n:
2582+ return
2583+ indices = range(r)
2584+ yield tuple(pool[i] for i in indices)
2585+ while True:
2586+ for i in reversed(range(r)):
2587+ if indices[i] != i + n - r:
2588+ break
2589+ else:
2590+ return
2591+ indices[i] += 1
2592+ for j in range(i+1, r):
2593+ indices[j] = indices[j-1] + 1
2594+ yield tuple(pool[i] for i in indices)
2595+
2596+
2597+class account_move_line(osv.osv):
2598+ '''
2599+ Show unreconciled amount on partial reconciled/none reconciled move line
2600+ '''
2601+ _inherit = 'account.move.line'
2602+ def _unreconciled(self, cr, uid, ids, prop, unknow_none, context):
2603+ '''
2604+ Calculate Amount yet to reconcile
2605+ '''
2606+ res={}
2607+ for line in self.browse(cr, uid, ids, context=context):
2608+ res[line.id] = line.debit - line.credit
2609+ if line.reconcile_partial_id:
2610+ res[line.id] = 0
2611+ for partial in line.reconcile_partial_id.line_partial_ids:
2612+ res[line.id] += partial.debit - partial.credit
2613+ res[line.id] = abs(res[line.id])
2614+ return res
2615+
2616+ def write(self, cr, uid, ids, vals, context=None, check=True, update_check=True):
2617+ if type(ids)!=type([]):
2618+ ids = [ids]
2619+ return super(account_move_line, self).write(cr, uid, ids, vals, context=context, check=check, update_check=update_check)
2620+
2621+ _columns = {
2622+ 'amount_unreconciled': fields.function(_unreconciled, method=True, string='Unreconciled Amount'),
2623+ }
2624+account_move_line()
2625+
2626+class account_voucher(osv.osv):
2627+
2628+
2629+ def default_get(self, cr, user, fields_list, context=None):
2630+ """
2631+ Returns default values for fields
2632+ @param fields_list: list of fields, for which default values are required to be read
2633+ @param context: context arguments, like lang, time zone
2634+
2635+ @return: Returns a dict that contains default values for fields
2636+ """
2637+ values = super(account_voucher, self).default_get(cr, user, fields_list, context=context)
2638+ if values.get('type') == 'payment':
2639+ company = self.pool.get('res.users').browse(cr, user, user, context=context).company_id
2640+ if company.def_supp_journal:
2641+ values['journal_id'] = company.def_supp_journal.id
2642+ return values
2643+
2644+
2645+ def _find_exact_match(self, cr, uid, lines, amount, mark_pay=False, use_discount=False, context=False):
2646+ '''
2647+ Accept voucher lines as list of dict and amount to match
2648+ return : update lines if it found an exact match
2649+ '''
2650+ total_amount = 0.0
2651+ discount_available = {}
2652+ total_discount_available = 0.0
2653+ for line in lines:
2654+ if mark_pay or line.pay == True:
2655+ if use_discount:
2656+ discount_available[line.id] = 0.0
2657+ for discount_line in line.available_discounts:
2658+ discount_available[line.id] += discount_line.proposed_discount
2659+ total_discount_available += discount_line.proposed_discount
2660+ total_amount += line.amount_unreconciled - line.credit_used - (line._columns.has_key('writeoff_amount') and line.writeoff_amount)
2661+ else:
2662+ total_amount += line.amount_unreconciled - line.credit_used - line.discount_used- (line._columns.has_key('writeoff_amount') and line.writeoff_amount)
2663+ if abs(amount - total_amount) < 0.01 :
2664+ '''
2665+ Unchecking all the lines other than matched line
2666+ '''
2667+ line_ids = self.read(cr,uid,lines[0].voucher_id.id,['line_ids'])['line_ids']
2668+ self.pool.get('account.voucher.line').write(cr,uid,line_ids,{'pay':False})
2669+ for line in lines:
2670+ if mark_pay or line.pay == True:
2671+ line.write({'amount':line.amount_unreconciled - line.credit_used - line.discount_used - (line._columns.has_key('writeoff_amount') and line.writeoff_amount), 'pay':True })
2672+ else:
2673+ line.write({'amount':0.0, 'pay':False })
2674+ return True
2675+ if use_discount and total_amount>amount and (total_amount - amount ) <=total_discount_available: #It is possible to match using Discount
2676+ '''
2677+ Unchecking all the lines other than matched line and uncheck all discount lines
2678+ '''
2679+ line_ids = self.read(cr,uid,lines[0].voucher_id.id,['line_ids'])['line_ids']
2680+ self.pool.get('account.voucher.line').write(cr,uid,line_ids,{'pay':False})
2681+ for line_id in line_ids:
2682+ discount_ids = self.pool.get('account.voucher.line').read(cr,uid,line_id,['available_discounts'])['available_discounts']
2683+ self.pool.get("account.voucher.line.discount_to_use").write(cr,uid,discount_ids,{'use_discount':False, 'discount_amount':0.0})
2684+ discount_to_use = total_amount - amount
2685+ for line in lines:
2686+ if discount_to_use > 0.0:
2687+ for discount_line in line.available_discounts:
2688+ discount_line.write({'use_discount':True,'discount_amount':min(discount_to_use, discount_line.proposed_discount)})
2689+ discount_to_use = discount_to_use - min(discount_to_use, discount_line.proposed_discount)
2690+ line = self.pool.get('account.voucher.line').browse(cr, uid, line.id,context=context)
2691+ line.write({'amount':line.amount_unreconciled - line.credit_used - line.discount_used - (line._columns.has_key('writeoff_amount') and line.writeoff_amount), 'pay':True })
2692+ return True
2693+ if len(lines) > 1:
2694+ for combination in _combinations(lines,len(lines)-1 ):
2695+ if self._find_exact_match(cr, uid, combination, amount, mark_pay, context=False):
2696+ return True
2697+ return False
2698+
2699+ def calc_diff(self, cr, uid, ids, context={}):
2700+ '''
2701+ Called by calculate/re-calculate action.
2702+ This method will update the credit lines on voucher lines.
2703+ If the field "auto_match" marked, this method will run a matching routine
2704+ '''
2705+ res = {'nodestroy':True}
2706+ amount_cash_discount = 0.0
2707+ amount_interest = 0.0
2708+ for vch in self.browse(cr, uid, ids):
2709+ for line in vch.line_cr_ids:
2710+ '''
2711+ Update the credit lines and discount lines so that matching routine can use the latest available credits and discounts
2712+ '''
2713+ line._update_credit_lines( context=context)
2714+ line._update_discount_lines(context=context)
2715+
2716+ if vch.auto_match:
2717+ '''
2718+ Try the matching routine including the manual selection
2719+ '''
2720+ ret = self._find_exact_match(cr, uid, vch.line_cr_ids, vch.amount, mark_pay=False, use_discount=False, context=False)
2721+ if not ret:
2722+ '''
2723+ Try to match considering all voucher lines
2724+ '''
2725+ ret = self._find_exact_match(cr, uid, vch.line_cr_ids, vch.amount, mark_pay=True, use_discount=False, context=False)
2726+ if not ret:
2727+ '''
2728+ Try to match considering discount
2729+ '''
2730+ ret = self._find_exact_match(cr, uid, vch.line_cr_ids, vch.amount, mark_pay=True, use_discount=True, context=False)
2731+ return res
2732+
2733+ def onchange_date(self, cr, user, ids, partner_id, journal_id, price, currency_id, ttype, date, context={}):
2734+ """
2735+ @param date: latest value from user input for field date
2736+ @param args: other arguments
2737+ @param context: context arguments, like lang, time zone
2738+ @return: Returns a dict which contains new values, and context
2739+ """
2740+ period_pool = self.pool.get('account.period')
2741+ pids = period_pool.search(cr, user, [('date_start','<=',date), ('date_stop','>=',date)])
2742+ if not pids:
2743+ return {}
2744+ return {
2745+ 'value':{
2746+ 'period_id':pids[0]
2747+ }
2748+ }
2749+
2750+ def _update_discounts(self, lines, vch_date):
2751+ # hook method for account_cash_discount_us module
2752+ return True
2753+
2754+ _inherit = 'account.voucher'
2755+ _columns = {
2756+ 'number': fields.related('move_id', 'name', type="char", readonly=True, string='Number'),
2757+ 'auto_match':fields.boolean('Use Automatic Matching')
2758+ }
2759+
2760+ def onchange_partner_id(self, cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, date, context={}):
2761+ """price
2762+ Returns a dict that contains new values and context
2763+
2764+ @param partner_id: latest value from user input for field partner_id
2765+ @param args: other arguments
2766+ @param context: context arguments, like lang, time zone
2767+
2768+ @return: Returns a dict which contains new values, and context
2769+ """
2770+ '''
2771+ function ported from account_voucher_jds module
2772+ This function is redefined and do not use functionality provided by account_voucher module
2773+ Possible issue is that the latest function from updated account_voucher module will have no effect if this module is installed
2774+ '''
2775+ if context is None:
2776+ context = {}
2777+ currency_pool = self.pool.get('res.currency')
2778+ move_pool = self.pool.get('account.move')
2779+ line_pool = self.pool.get('account.voucher.line')
2780+ move_line_pool = self.pool.get('account.move.line')
2781+ partner_pool = self.pool.get('res.partner')
2782+ journal_pool = self.pool.get('account.journal')
2783+ default = {
2784+ 'value':{'line_ids':[], 'line_dr_ids':[], 'line_cr_ids':[], 'pre_line': False, 'currency_id':currency_id, 'journal_id': False},
2785+ }
2786+ if partner_id and not journal_id:
2787+ partner = partner_pool.browse(cr, uid, partner_id, context)
2788+ if partner._columns.has_key('payment_meth_id') and partner.payment_meth_id:
2789+ payment_mode_pool = self.pool.get('payment.mode')
2790+ payment_meth = payment_mode_pool.browse(cr, uid, partner.payment_meth_id.id, context)
2791+ if payment_meth:
2792+ default['value']['journal_id'] = payment_meth.journal.id
2793+ journal_id = payment_meth.journal.id
2794+ if not journal_id:
2795+ return {}
2796+ context_multi_currency = context.copy()
2797+ if date:
2798+ context_multi_currency.update({'date': date})
2799+
2800+ vals = self.onchange_journal(cr, uid, ids, journal_id, [], False, partner_id, context)
2801+ vals = vals.get('value')
2802+ currency_id = vals.get('currency_id', currency_id)
2803+
2804+ if not partner_id:
2805+ return default
2806+
2807+ if not partner_id and ids:
2808+ line_ids = line_pool.search(cr, uid, [('voucher_id','=',ids[0])])
2809+ if line_ids:
2810+ line_pool.unlink(cr, uid, line_ids)
2811+ return default
2812+
2813+ journal = journal_pool.browse(cr, uid, journal_id, context=context)
2814+ partner = partner_pool.browse(cr, uid, partner_id, context=context)
2815+ account_id = False
2816+ if journal.type in ('sale','sale_refund'):
2817+ account_id = partner.property_account_receivable.id
2818+ elif journal.type in ('purchase', 'purchase_refund','expense'):
2819+ account_id = partner.property_account_payable.id
2820+ elif journal.type in ('cash', 'bank'):
2821+ account_id = journal.default_debit_account_id.id
2822+ else:
2823+ account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id
2824+
2825+ if journal and not default['value']['currency_id']:
2826+ default['value']['currency_id'] = journal.company_id.currency_id.id
2827+ default['value']['account_id'] = account_id
2828+ if journal.type not in ('cash', 'bank'):
2829+ return default
2830+
2831+ total_credit = 0.0
2832+ total_debit = 0.0
2833+ account_type = 'receivable'
2834+ if ttype == 'payment':
2835+ account_type = 'payable'
2836+ total_debit = price or 0.0
2837+ else:
2838+ total_credit = price or 0.0
2839+ account_type = 'receivable'
2840+
2841+ if partner._columns.has_key('nat_acc_parent') and partner.nat_acc_parent:
2842+ partner_ids = partner_pool.search(cr,uid,[('parent_id','child_of',[partner_id])])
2843+ else:
2844+ partner_ids = [partner_id]
2845+ ids = move_line_pool.search(cr, uid, [('account_id.type','=', account_type), ('reconcile_id','=', False), ('partner_id','in',partner_ids)], context=context)
2846+ ids.reverse()
2847+ moves = move_line_pool.browse(cr, uid, ids)
2848+
2849+ company_currency = journal.company_id.currency_id.id
2850+ if company_currency != currency_id and ttype == 'payment':
2851+ total_debit = currency_pool.compute(cr, uid, currency_id, company_currency, total_debit, context=context_multi_currency)
2852+ elif company_currency != currency_id and ttype == 'receipt':
2853+ total_credit = currency_pool.compute(cr, uid, currency_id, company_currency, total_credit, context=context_multi_currency)
2854+
2855+ for line in moves:
2856+ if line.credit and line.reconcile_partial_id and ttype == 'receipt':
2857+ continue
2858+ if line.debit and line.reconcile_partial_id and ttype == 'payment':
2859+ continue
2860+ total_credit += line.credit or 0.0
2861+ total_debit += line.debit or 0.0
2862+
2863+ for line in moves:
2864+ if line.credit and line.reconcile_partial_id and ttype == 'receipt':
2865+ continue
2866+ if line.debit and line.reconcile_partial_id and ttype == 'payment':
2867+ continue
2868+
2869+ original_amount = line.credit or line.debit or 0.0
2870+ rs = {
2871+ 'name':line.move_id.name,
2872+ 'type': line.credit and 'dr' or 'cr',
2873+ 'move_line_id':line.id,
2874+ 'account_id':line.account_id.id,
2875+ 'amount_original':currency_pool.compute(cr, uid, company_currency, currency_id, original_amount, context=context_multi_currency),
2876+ 'date_original':line.date,
2877+ 'date_due':line.date_maturity,
2878+ 'amount_unreconciled':currency_pool.compute(cr, uid, company_currency, currency_id, line.amount_unreconciled, context=context_multi_currency)
2879+ }
2880+
2881+ def calc_amount(line, total):
2882+ return min(line.amount_unreconciled, total)
2883+
2884+ if line.credit:
2885+ amount = calc_amount(line, total_debit)
2886+ rs['amount'] = currency_pool.compute(cr, uid, company_currency, currency_id, amount, context=context_multi_currency)
2887+ total_debit -= amount
2888+ else:
2889+ amount = calc_amount(line, total_credit)
2890+ rs['amount'] = currency_pool.compute(cr, uid, company_currency, currency_id, amount, context=context_multi_currency)
2891+ total_credit -= amount
2892+
2893+ default['value']['line_ids'].append(rs)
2894+ if rs['type'] == 'cr':
2895+ default['value']['line_cr_ids'].append(rs)
2896+ else:
2897+ default['value']['line_dr_ids'].append(rs)
2898+
2899+ if ttype == 'payment' and len(default['value']['line_cr_ids']) > 0:
2900+ default['value']['pre_line'] = 1
2901+ elif ttype == 'receipt' and len(default['value']['line_dr_ids']) > 0:
2902+ default['value']['pre_line'] = 1
2903+ for credit_line in default['value']['line_dr_ids']:
2904+ credit_line['amount'] = 0.0
2905+ for invoice_line in default['value']['line_cr_ids']:
2906+ invoice_line['amount'] = 0.0
2907+ invoice_line['amount_difference'] = invoice_line['amount_unreconciled']
2908+
2909+ return default
2910+
2911+ def calc_cash_discount(self, vch, line, amount=0):
2912+ # hook method for cash discount
2913+ return 0.0
2914+
2915+ def calc_interest(self, vch, line, amount=0):
2916+ # hook method for interest posting
2917+ return 0.0
2918+
2919+ def action_move_line_create(self, cr, uid, ids, context=None):
2920+ '''
2921+ Function overridden from account voucher module
2922+ account posting include amount after currency conversion
2923+ TODO : Function need more analysis and testing
2924+ '''
2925+
2926+ def _get_payment_term_lines(term_id, amount):
2927+ term_pool = self.pool.get('account.payment.term')
2928+ if term_id and amount:
2929+ terms = term_pool.compute(cr, uid, term_id, amount)
2930+ return terms
2931+ return False
2932+ if not context:
2933+ context = {}
2934+ move_pool = self.pool.get('account.move')
2935+ move_line_pool = self.pool.get('account.move.line')
2936+ analytic_pool = self.pool.get('account.analytic.line')
2937+ currency_pool = self.pool.get('res.currency')
2938+ invoice_pool = self.pool.get('account.invoice')
2939+ company_pool = self.pool.get('res.company')
2940+ for vch in self.browse(cr, uid, ids):
2941+ if vch.move_id:
2942+ continue
2943+
2944+ if 'force_name' in context and context['force_name']:
2945+ name = context['force_name']
2946+ elif vch.journal_id.sequence_id:
2947+ name = self.pool.get('ir.sequence').get_id(cr, uid, vch.journal_id.sequence_id.id)
2948+ else:
2949+ raise osv.except_osv(_('Error !'), _('Please define a sequence on the journal !'))
2950+
2951+ move = {
2952+ 'name' : name,
2953+ 'journal_id': vch.journal_id.id,
2954+ 'narration' : vch.narration,
2955+ 'date':vch.date,
2956+ 'ref':vch.reference,
2957+ 'period_id': vch.period_id and vch.period_id.id or False
2958+ }
2959+ move_id = move_pool.create(cr, uid, move)
2960+
2961+ #create the first line manually
2962+ company_currency = vch.journal_id.company_id.currency_id.id
2963+ debit = 0.0
2964+ credit = 0.0
2965+ # TODO: is there any other alternative then the voucher type ??
2966+ # -for sale, purchase we have but for the payment and receipt we do not have as based on the bank/cash journal we can not know its payment or receipt
2967+ if vch.type in ('purchase', 'payment'):
2968+ credit = currency_pool.compute(cr, uid, vch.currency_id.id, company_currency, vch.amount)
2969+ elif vch.type in ('sale', 'receipt'):
2970+ debit = currency_pool.compute(cr, uid, vch.currency_id.id, company_currency, vch.amount)
2971+ if debit < 0:
2972+ credit = -debit
2973+ debit = 0.0
2974+ if credit < 0:
2975+ debit = -credit
2976+ credit = 0.0
2977+ move_line = {
2978+ 'name':vch.name or '/',
2979+ 'debit':debit,
2980+ 'credit':credit,
2981+ 'account_id':vch.account_id.id,
2982+ 'move_id':move_id ,
2983+ 'journal_id':vch.journal_id.id,
2984+ 'period_id':vch.period_id.id,
2985+ 'partner_id':vch.partner_id.id,
2986+ 'currency_id':vch.currency_id.id,
2987+ 'amount_currency':vch.amount,
2988+ 'date':vch.date,
2989+ 'date_maturity':vch.date_due
2990+ }
2991+
2992+ if (debit == 0.0 or credit == 0.0 or debit+credit > 0) and (debit > 0.0 or credit > 0.0):
2993+ master_line = move_line_pool.create(cr, uid, move_line)
2994+ company = company_pool.browse(cr, uid, vch.company_id.id, context)
2995+ rec_list_ids = []
2996+ line_total = debit - credit
2997+ if vch.type == 'sale':
2998+ line_total = line_total - currency_pool.compute(cr, uid, vch.currency_id.id, company_currency, vch.tax_amount)
2999+ elif vch.type == 'purchase':
3000+ line_total = line_total + currency_pool.compute(cr, uid, vch.currency_id.id, company_currency, vch.tax_amount)
3001+
3002+ writeoff_ids = []
3003+ for line in vch.line_ids:
3004+ if not line.amount:
3005+ skip_flag = True
3006+ if line.available_credits:
3007+ for av_credits in line.available_credits:
3008+ if av_credits.use_credit and av_credits.discount_amount:
3009+ skip_flag=False
3010+ if skip_flag:
3011+ continue
3012+ amount_cash_discount = 0.0#self.calc_cash_discount(line, line.amount)
3013+ amount_writeoff = 0.0#calc_writeoff(line, amount_cash_discount)
3014+ amount_interest = 0.0#calc_interest(line, amount_cash_discount + amount_writeoff)
3015+ amount_currency = currency_pool.compute(cr, uid, vch.currency_id.id, company_currency, line.amount)
3016+ amount = amount_currency
3017+ move_line = {
3018+ 'journal_id':vch.journal_id.id,
3019+ 'period_id':vch.period_id.id,
3020+ 'name':line.name and line.name or '/',
3021+ 'account_id':line.account_id.id,
3022+ 'move_id':move_id,
3023+ 'partner_id':vch.partner_id.id,
3024+ 'currency_id':vch.currency_id.id,
3025+ #Posting amount after currency conversion
3026+ 'amount_currency':amount,
3027+ 'analytic_account_id':line.account_analytic_id and line.account_analytic_id.id or False,
3028+ 'quantity':1,
3029+ 'credit':0.0,
3030+ 'debit':0.0,
3031+ 'date':vch.date
3032+ }
3033+ if line.invoice_id:
3034+ move_line['partner_id'] = line.invoice_id.partner_id.id
3035+ if amount < 0:
3036+ amount = -amount
3037+ if line.type == 'dr':
3038+ line.type = 'cr'
3039+ else:
3040+ line.type = 'dr'
3041+
3042+ if (line.type=='dr'):
3043+ line_total += amount
3044+ move_line['debit'] = amount
3045+ else:
3046+ line_total -= (amount+line.credit_used)
3047+ move_line['credit'] = (amount+line.credit_used)
3048+
3049+ if vch.tax_id and vch.type in ('sale', 'purchase'):
3050+ move_line.update({
3051+ 'account_tax_id':vch.tax_id.id,
3052+ })
3053+
3054+ if line_total < 0.01 and line_total > -0.01:
3055+ line_total = 0.0
3056+
3057+ master_line = move_line_pool.create(cr, uid, move_line)
3058+ if line.move_line_id.id:
3059+ rec_ids = [master_line, line.move_line_id.id]
3060+ rec_list_ids.append(rec_ids)
3061+
3062+ if line.invoice_id.id:
3063+ invoice = self.pool.get('account.invoice').browse(cr, uid, line.invoice_id.id)
3064+ types = {'out_invoice': -1, 'in_invoice': 1, 'out_refund': 1, 'in_refund': -1}
3065+ direction = types[invoice.type]
3066+
3067+ '''
3068+ Post Discount Lines for supplier
3069+ '''
3070+
3071+ if line.type == 'dr' and line._columns.has_key('available_discounts') and line.available_discounts: # and line.cash_discount > 0.0:
3072+ for available_discounts in line.available_discounts:
3073+ if available_discounts.use_discount:
3074+ l1 = {
3075+ 'journal_id':vch.journal_id.id,
3076+ 'period_id':vch.period_id.id,
3077+ 'debit': direction * available_discounts.discount_amount>0 and direction * available_discounts.discount_amount,
3078+ 'credit': direction * available_discounts.discount_amount<0 and - direction * available_discounts.discount_amount,
3079+ 'account_id': line.account_id.id,
3080+ 'partner_id': line.move_line_id.partner_id.id, #line.partner_id.id,
3081+ 'move_id':move_id,
3082+ 'ref':'Cash Discount',
3083+ 'name':line.name and line.name or '/',
3084+ 'date': vch.date,
3085+ 'currency_id':vch.currency_id.id,
3086+ 'amount_currency':available_discounts.discount_amount and - direction * available_discounts.discount_amount or 0.0,
3087+ 'company_id': line.company_id.id,
3088+ }
3089+ l2 = {
3090+ 'journal_id':vch.journal_id.id,
3091+ 'period_id':vch.period_id.id,
3092+ 'debit': direction * available_discounts.discount_amount<0 and - direction * available_discounts.discount_amount,
3093+ 'credit': direction * available_discounts.discount_amount>0 and direction * available_discounts.discount_amount,
3094+ 'account_id': available_discounts.gl_account.id,
3095+ 'partner_id': line.move_line_id.partner_id.id,
3096+ 'move_id':move_id,
3097+ 'ref':'Cash Discount',
3098+ 'name':line.name and line.name or '/',
3099+ 'date': vch.date,
3100+ 'currency_id':vch.currency_id.id,
3101+ 'amount_currency':available_discounts.discount_amount and - direction * available_discounts.discount_amount or 0.0,
3102+ 'company_id': line.company_id.id,
3103+ }
3104+ name = invoice.number
3105+ l1['name'] = name
3106+ l2['name'] = name
3107+ move_lines = [l1,l2]
3108+ for move_line in move_lines:
3109+ master_line = move_line_pool.create(cr, uid, move_line)
3110+ if line.move_line_id.id and move_line['account_id'] == line.move_line_id.account_id.id:
3111+ rec_ids = [master_line, line.move_line_id.id]
3112+ rec_list_ids.append(rec_ids)
3113+
3114+
3115+
3116+ '''
3117+ Post Discount Lines for customer
3118+ '''
3119+
3120+
3121+ if line.type == 'cr' and line._columns.has_key('available_discounts') and line.available_discounts: # and line.cash_discount > 0.0:
3122+ for available_discounts in line.available_discounts:
3123+ if available_discounts.use_discount:
3124+ l1 = {
3125+ 'journal_id':vch.journal_id.id,
3126+ 'period_id':vch.period_id.id,
3127+ 'debit': direction * available_discounts.discount_amount>0 and direction * available_discounts.discount_amount,
3128+ 'credit': direction * available_discounts.discount_amount<0 and - direction * available_discounts.discount_amount,
3129+ 'account_id': line.account_id.id,
3130+ 'partner_id': line.move_line_id.partner_id.id, #line.partner_id.id,
3131+ 'move_id':move_id,
3132+ 'ref':'Cash Discount',
3133+ 'name':line.name and line.name or '/',
3134+ 'date': vch.date,
3135+ 'currency_id':vch.currency_id.id,
3136+ 'amount_currency':available_discounts.discount_amount and - direction * available_discounts.discount_amount or 0.0,
3137+ 'company_id': line.company_id.id,
3138+ }
3139+ l2 = {
3140+ 'journal_id':vch.journal_id.id,
3141+ 'period_id':vch.period_id.id,
3142+ 'debit': direction * available_discounts.discount_amount<0 and - direction * available_discounts.discount_amount,
3143+ 'credit': direction * available_discounts.discount_amount>0 and direction * available_discounts.discount_amount,
3144+ 'account_id': available_discounts.gl_account.id,
3145+ 'partner_id': line.move_line_id.partner_id.id,
3146+ 'move_id':move_id,
3147+ 'ref':'Cash Discount',
3148+ 'name':line.name and line.name or '/',
3149+ 'date': vch.date,
3150+ 'currency_id':vch.currency_id.id,
3151+ 'amount_currency':available_discounts.discount_amount and - direction * available_discounts.discount_amount or 0.0,
3152+ 'company_id': line.company_id.id,
3153+ }
3154+ name = invoice.number
3155+ l1['name'] = name
3156+ l2['name'] = name
3157+ move_lines = [l1,l2]
3158+ for move_line in move_lines:
3159+ master_line = move_line_pool.create(cr, uid, move_line)
3160+ if line.move_line_id.id and move_line['account_id'] == line.move_line_id.account_id.id:
3161+ rec_ids = [master_line, line.move_line_id.id]
3162+ rec_list_ids.append(rec_ids)
3163+ '''
3164+ Post Writeoff
3165+ '''
3166+ if line.type == 'cr' and line._columns.has_key('writeoff_ids') and line.writeoff_ids:
3167+ for writeoff in line.writeoff_ids:
3168+ l1 = {
3169+ 'journal_id':vch.journal_id.id,
3170+ 'period_id':vch.period_id.id,
3171+ 'debit': direction * writeoff.writeoff_amount>0 and direction * writeoff.writeoff_amount,
3172+ 'credit': direction * writeoff.writeoff_amount<0 and - direction * writeoff.writeoff_amount,
3173+ 'account_id': line.account_id.id,
3174+ 'partner_id': line.move_line_id.partner_id.id, #,
3175+ 'move_id':move_id,
3176+ 'ref':'Writeoff',
3177+ 'name':line.name and line.name or '/',
3178+ 'date': vch.date,
3179+ 'currency_id':vch.currency_id.id,
3180+ 'amount_currency':writeoff.writeoff_amount and - direction * writeoff.writeoff_amount or 0.0,
3181+ 'company_id': line.company_id.id,
3182+ }
3183+ l2 = {
3184+ 'journal_id':vch.journal_id.id,
3185+ 'period_id':vch.period_id.id,
3186+ 'debit': direction * writeoff.writeoff_amount<0 and - direction * writeoff.writeoff_amount,
3187+ 'credit': direction * writeoff.writeoff_amount>0 and direction * writeoff.writeoff_amount,
3188+ 'account_id': writeoff.gl_account.id,
3189+ 'partner_id': line.partner_id.id,
3190+ 'move_id':move_id,
3191+ 'ref':'Writeoff',
3192+ 'name':line.name and line.name or '/',
3193+ 'date': vch.date,
3194+ 'currency_id':vch.currency_id.id,
3195+ 'amount_currency':writeoff.writeoff_amount and - direction * writeoff.writeoff_amount or 0.0,
3196+ 'company_id': line.company_id.id,
3197+ }
3198+ name = invoice.number
3199+ l1['name'] = name
3200+ l2['name'] = name
3201+ move_lines = [l1,l2]
3202+ for move_line in move_lines:
3203+ master_line = move_line_pool.create(cr, uid, move_line)
3204+ if line.move_line_id.id and move_line['account_id'] == line.move_line_id.account_id.id:
3205+ rec_ids = [master_line, line.move_line_id.id]
3206+ rec_list_ids.append(rec_ids)
3207+
3208+ if line.type == 'dr':
3209+ line.write({ 'pending_credits':0})
3210+
3211+ diff = line_total
3212+ if not self.pool.get('res.currency').is_zero(cr, uid, vch.currency_id, line_total):
3213+ move_line = {
3214+ 'name':name,
3215+ 'account_id':False,
3216+ 'move_id':move_id ,
3217+ 'partner_id':vch.partner_id.id,
3218+ 'date':vch.date,
3219+ 'credit':diff>0 and diff or 0.0,
3220+ 'debit':diff<0 and -diff or 0.0,
3221+ }
3222+ account_id = False
3223+ if vch.type in ('sale', 'receipt'):
3224+ account_id = vch.partner_id.property_account_receivable.id
3225+ else:
3226+ account_id = vch.partner_id.property_account_payable.id
3227+ move_line['account_id'] = account_id
3228+ move_line_id = move_line_pool.create(cr, uid, move_line)
3229+# if line.move_line_id.id:
3230+# rec_ids = [move_line, line.move_line_id.id]
3231+# rec_list_ids.append(rec_ids)
3232+
3233+ self.write(cr, uid, [vch.id], {
3234+ 'move_id': move_id,
3235+ 'state':'posted'
3236+ })
3237+ move_pool.post(cr, uid, [move_id], context={})
3238+
3239+ # let's see if we can incorporate this in the reconciliation
3240+ #diff = 0
3241+ if diff > 0.0:
3242+ diff = line_total
3243+ rec_ids = []
3244+ debit_ids = []
3245+ applied_amount = 0.0
3246+ move = move_pool.browse(cr, uid, move_id, context={})
3247+ for line in move.line_id:
3248+ if line.account_id.id == vch.journal_id.default_credit_account_id.id:
3249+ applied_amount += line.debit
3250+ if line.id and line.credit > 0.0:
3251+ if line.id not in rec_ids:
3252+ rec_ids.append(line.id)
3253+ if line.id and line.debit > 0.0:
3254+ if line.id not in debit_ids:
3255+ debit_ids.append(line.id)
3256+ if applied_amount == diff:
3257+ for vchcr in vch.line_cr_ids:
3258+ if vchcr.amount and vchcr.move_line_id.id not in rec_ids:
3259+ rec_ids.append(vchcr.move_line_id.id)
3260+ for vchdr in vch.line_dr_ids:
3261+ if vchdr.amount and vchdr.move_line_id.id not in debit_ids:
3262+ debit_ids.append(vchdr.move_line_id.id)
3263+ rec_list_ids = [rec_ids, debit_ids]
3264+ for rec_ids in rec_list_ids:
3265+ if len(rec_ids) >= 2:
3266+ move_line_pool.reconcile_partial(cr, uid, rec_ids)
3267+ return True
3268+
3269+account_voucher()
3270+
3271+class account_voucher_line(osv.osv):
3272+ _inherit = 'account.voucher.line'
3273+ _order = "date_due"
3274+
3275+
3276+ def _update_credit_lines(self,cr, uid, ids, context):
3277+ '''
3278+ Create or update the list of credit that can be used under each voucher line.
3279+ To be called from the calculate/re-calculate action
3280+ '''
3281+ credits_used_pool = self.pool.get('account.voucher.line.credits_to_use')
3282+ for line in self.browse(cr , uid, ids, context):
3283+ credits_lines_used = [x.orginal_credit_line_id.id for x in line.available_credits]
3284+ for credit_line in line.voucher_id.line_dr_ids :
3285+ if credit_line.id not in credits_lines_used and line.invoice_id and line.invoice_id.payment_term:
3286+ #
3287+ credits_used_pool.create(cr, uid, {
3288+ 'voucher_line_id': line.id,
3289+ 'orginal_credit_line_id':credit_line.id,
3290+ 'use_credit': False,
3291+ 'inv_credit': credit_line.move_line_id.id,
3292+ 'discount_window_date': credit_line.date_original,
3293+ 'orginal_amount': credit_line.amount_original,
3294+ 'available_amount': credit_line.amount_unreconciled - credit_line.pending_credits,
3295+ 'discount_amount': 0.0,
3296+ 'gl_account' : credit_line.account_id.id,})
3297+ else :
3298+ to_update_credit_line_ids = credits_used_pool.search(cr,uid,[('voucher_line_id','=',line.id),( 'orginal_credit_line_id','=',credit_line.id)], context=context)
3299+ if to_update_credit_line_ids:
3300+ credits_used_pool.write(cr, uid,to_update_credit_line_ids,{'available_amount': credit_line.amount_unreconciled-credit_line.pending_credits}, context=context)
3301+
3302+
3303+ def write(self, cr, user, ids, vals, context=None):
3304+ '''
3305+ Add invoice and description in payment modification line
3306+ '''
3307+ if type(ids) == type([]):
3308+ move = self.browse(cr, user,ids[0]).move_line_id
3309+ else:
3310+ move = self.browse(cr, user,ids).move_line_id
3311+ if move:
3312+ vals['invoice_id'] = move.invoice and move.invoice.id
3313+ vals['name'] = move.invoice and move.invoice.number
3314+
3315+ return super(account_voucher_line, self).write(cr, user, ids, vals, context)
3316+
3317+ def create(self, cr, user, vals, context=None):
3318+ '''
3319+ Add invoice and description in payment modification line
3320+ '''
3321+ if vals.has_key('move_line_id') and vals['move_line_id']:
3322+ move = self.pool.get('account.move.line').browse(cr, user,vals['move_line_id'])
3323+ vals['invoice_id'] = move.invoice and move.invoice.id
3324+ vals['name'] = move.invoice and move.invoice.number
3325+ return super(account_voucher_line, self).create(cr, user, vals, context)
3326+
3327+ def calc_amt(self, cr, uid, ids, context={}):
3328+ '''
3329+ Function to calculate Pending Credits Used
3330+ can be removed : Not used anymore : verify first
3331+ '''
3332+ res = {}
3333+ result = 0.0
3334+
3335+ for id in ids:
3336+ res[id] = result
3337+ return res
3338+
3339+ def _credits_calc(self, cr, uid, ids, name, args, context):
3340+ '''
3341+ Function to calculate Pending Credits Used
3342+ '''
3343+ credits_used_pool = self.pool.get('account.voucher.line.credits_to_use')
3344+ res={}
3345+ for line in self.browse(cr, uid, ids):
3346+ credits_used_ids = credits_used_pool.search(cr, uid, [('orginal_credit_line_id','=',line.id)])
3347+ res[line.id] = 0.0
3348+ if line.voucher_id.state != 'draft':
3349+ continue
3350+ for credit_used in credits_used_pool.browse(cr, uid, credits_used_ids, context=context):
3351+ if credit_used.use_credit:
3352+ res[line.id] += credit_used.discount_amount
3353+ return res
3354+ def _compute_credit_used(self, cr, uid, ids, name, args, context=None):
3355+ '''
3356+ Field function for credit_used
3357+ '''
3358+ res = {}
3359+ for line in self.browse(cr, uid, ids):
3360+ res[line.id] = 0.0
3361+ for credit_line in line.available_credits:
3362+ if credit_line.use_credit :
3363+ res[line.id] += credit_line.discount_amount
3364+ return res
3365+
3366+ def _compute_balance(self, cr, uid, ids, name, args, context=None):
3367+ '''
3368+ Field function [multi] to calculate amount_original, amount_unreconciled and amount_difference
3369+ '''
3370+ currency_pool = self.pool.get('res.currency')
3371+ rs_data = {}
3372+ for line in self.browse(cr, uid, ids):
3373+ ctx = context.copy()
3374+ ctx.update({'date': line.voucher_id.date})
3375+ res = {}
3376+ company_currency = line.voucher_id.journal_id.company_id.currency_id.id
3377+ voucher_currency = line.voucher_id.currency_id.id
3378+ move_line = line.move_line_id or False
3379+ if not move_line:
3380+ res['amount_original'] = 0.0
3381+ res['amount_unreconciled'] = 0.0
3382+
3383+ elif move_line and move_line.credit > 0:
3384+ res['amount_original'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, move_line.credit, context=ctx)
3385+ else:
3386+ res['amount_original'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, move_line.debit, context=ctx)
3387+
3388+ if move_line:
3389+ res['amount_unreconciled'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, move_line.amount_unreconciled - line.pending_credits , context=ctx)
3390+ if line.amount > 0.0:
3391+ res['amount_difference'] = res['amount_unreconciled'] - line.amount - line.credit_used
3392+ else:
3393+ res['amount_difference'] = 0.0
3394+ rs_data[line.id] = res
3395+ return rs_data
3396+
3397+ def _get_due_date(self, cr, uid, ids, context=None):
3398+ '''
3399+ Store function to identify the voucher lines that need recalculation of date_due in the case of any change on account move line
3400+ Fixme: make sure that it return a list of voucher line id only
3401+ '''
3402+ result = {}
3403+ for line in self.pool.get('account.move.line').browse(cr, uid, ids, context=context):
3404+# result[line.invoice_id.id] = True ##Changed by Jabir to fix error when clicking Post button from Customer Payment form. 2010/11/24
3405+ result[line.invoice.id] = True
3406+ return result.keys()
3407+
3408+ def _compute_discount_used(self, cr, uid, ids, name, args, context=None):
3409+ '''
3410+ Field function to calculate discount_used
3411+ Hook function : to be redefined on account_cash_discount_us
3412+ '''
3413+ res = {}
3414+ for id in ids:
3415+ res[id] = 0.0
3416+ return res
3417+ def _calc_writeoff(self, cr, uid, ids, name, args, context):
3418+ '''
3419+ Field function to calculate writeoff_amount
3420+ Hook function : to be redefined on account_voucher_writeoff_us
3421+ '''
3422+ res={}
3423+ for id in ids:
3424+ res[id] = 0.0
3425+ return res
3426+
3427+
3428+ def _order_compute_credit_used(self, cr, uid, ids, context=None):
3429+ """
3430+ Store function - Field : credit_used, model : account.voucher.line.credits_to_use
3431+ """
3432+ operation_ids = []
3433+ for credits_to_use in self.pool.get('account.voucher.line.credits_to_use').browse(cr,uid,ids,context=context):
3434+ operation_ids.append(credits_to_use.voucher_line_id and credits_to_use.voucher_line_id.id)
3435+ return operation_ids
3436+
3437+ def _get_voucher_line_ids(self, cr, uid, ids, context=None):
3438+ '''
3439+ Store function - field : company_id, model :account.voucher
3440+ '''
3441+ result = []
3442+ for vch_lines in self.pool.get('account.voucher').read(cr, uid, ids, ['line_ids'], context=context):
3443+ result +=vch_lines['line_ids']
3444+ return result
3445+
3446+ _columns = {
3447+ 'invoice_id': fields.many2one('account.invoice', 'Invoice'),
3448+ 'account_id':fields.many2one('account.account','G/L Account', required=True),
3449+ 'amount':fields.float('Payment Amt', digits=(14,2), required=True),
3450+ 'date_due': fields.related('move_line_id','date_maturity', type='date', relation='account.move.line', string='Due Date', readonly=True , store={
3451+ 'account.voucher.line': (lambda self, cr, uid, ids, c={}: ids, ['move_line_id'], 20),
3452+ 'account.move.line': (_get_due_date, ['date_maturity'], 20),
3453+ }),
3454+ 'amount_original': fields.function(_compute_balance, method=True, multi='dc', type='float', string='Original Amt', store=True, readonly=True),
3455+ 'amount_unreconciled': fields.function(_compute_balance, method=True, multi='dc', type='float', string='Amt Due', store=True, readonly=True),
3456+ 'company_id': fields.related('voucher_id','company_id',type='many2one', relation='res.company', string='Company', store={
3457+ 'account.voucher': (_get_voucher_line_ids, ['company_id'], 20),
3458+ }),
3459+ 'credit_used': fields.function(_compute_credit_used, method=True, type='float', string='Credit Used',
3460+ store={'account.voucher.line.credits_to_use':(_order_compute_credit_used,['use_credit','discount_amount'], 10)}, readonly=True),
3461+ 'amount_difference': fields.function(_compute_balance, method=True, multi='dc', type='float', string='Unpaid Amt', digits=(16, 2) ),
3462+ 'writeoff': fields.boolean("Writeoff"),
3463+ 'pay': fields.boolean("Pay", required=True),
3464+ 'pending_credits':fields.function(_credits_calc, method=True, string='Pending Credits Used', type='float'),
3465+ 'available_credits':fields.one2many('account.voucher.line.credits_to_use', 'voucher_line_id', 'Available credits' ),
3466+ 'discount_used': fields.function(_compute_discount_used, method=True, type='float', string='Discount Used', store=False, readonly=True),
3467+ 'writeoff_amount':fields.function(_calc_writeoff, method=True, string='Write-off Amt', type='float', store=False,),
3468+ }
3469+
3470+ def recalculate_values(self, cr, uid, ids, context={}):
3471+ '''
3472+ Re-calculate button action
3473+ '''
3474+ if type(ids) == type([]):
3475+ voucher_line = self.browse(cr,uid,ids[0])
3476+ else:
3477+ voucher_line = self.browse(cr,uid,ids)
3478+ self.pool.get('account.voucher').calc_diff(cr, uid, [voucher_line.voucher_id.id])
3479+ return True
3480+ def clear_values(self, cr, uid, ids, context={}):
3481+ '''
3482+ Clear all selected discounts, credits and writeoffs and manually entered values
3483+ '''
3484+ voucher_line = self.browse(cr,uid,ids[0])
3485+ if voucher_line._columns.has_key('writeoff_ids'):
3486+ if voucher_line._columns.has_key('available_discounts'):
3487+ for lines in self.read(cr,uid,ids,['available_credits','writeoff_ids','available_discounts']):
3488+ lines['writeoff_ids'] and self.pool.get('account.voucher.line.writeoff').unlink(cr,uid,lines['writeoff_ids'])
3489+ lines['available_credits'] and self.pool.get('account.voucher.line.credits_to_use').write(cr,uid,lines['available_credits'],
3490+ {'use_credit':False, 'discount_amount':0.0})
3491+ lines['available_discounts'] and self.pool.get('account.voucher.line.discount_to_use').write(cr,uid,lines['available_discounts'],
3492+ {'use_discount':False, 'discount_amount':0.0})
3493+ else:
3494+ for lines in self.read(cr,uid,ids,['available_credits','writeoff_ids']):
3495+ lines['writeoff_ids'] and self.pool.get('account.voucher.line.writeoff').unlink(cr,uid,lines['writeoff_ids'])
3496+ lines['available_credits'] and self.pool.get('account.voucher.line.credits_to_use').write(cr,uid,lines['available_credits'],
3497+ {'use_credit':False, 'discount_amount':0.0})
3498+ elif voucher_line._columns.has_key('available_discounts'):
3499+ for lines in self.read(cr,uid,ids,['available_credits','available_discounts']):
3500+ lines['available_credits'] and self.pool.get('account.voucher.line.credits_to_use').write(cr,uid,lines['available_credits'],
3501+ {'use_credit':False, 'discount_amount':0.0})
3502+ lines['available_discounts'] and self.pool.get('account.voucher.line.discount_to_use').write(cr,uid,lines['available_discounts'],
3503+ {'use_discount':False, 'discount_amount':0.0})
3504+ else:
3505+ for lines in self.read(cr,uid,ids,['available_credits']):
3506+ lines['available_credits'] and self.pool.get('account.voucher.line.credits_to_use').write(cr,uid,lines['available_credits'],
3507+ {'use_credit':False, 'discount_amount':0.0})
3508+ return True
3509+
3510+ def onchange_pay(self, cr, uid, ids, line_amount, pay, amount_unreconciled,par_cr_ids, par_amount, credit_used, discount_used=0, writeoff_amount=0, context={}):
3511+ '''
3512+ Function to automatically fill the values when the pay checkbox is selected
3513+ '''
3514+ ret = {}
3515+ writeoff_amount = (not writeoff_amount and [0] or [writeoff_amount])[0]
3516+ discount_used = (not discount_used and [0] or [discount_used])[0]
3517+ credit_used = (not credit_used and [0] or [credit_used])[0]
3518+ if pay:
3519+ tot_amt = par_amount + line_amount
3520+ for credit in par_cr_ids:
3521+ if credit[2]['pay']:
3522+ tot_amt -= (credit[2]['amount'])
3523+ if tot_amt < 0:
3524+ ret['amount'] = 0.0
3525+ else:
3526+ amount_unreconciled -= (discount_used+writeoff_amount+credit_used)
3527+ ret['amount'] = min(tot_amt,(amount_unreconciled<0) and 0 or amount_unreconciled)
3528+ else:
3529+ ret['amount'] = 0.0
3530+ return {'value':ret}
3531+
3532+
3533+
3534+account_voucher_line()
3535+
3536+class account_bank_statement(osv.osv):
3537+ _inherit = 'account.bank.statement'
3538+
3539+ def create_move_from_st_line(self, cr, uid, st_line_id, company_currency_id, next_number, context=None):
3540+ '''
3541+ TODO : Need careful testing of this function.
3542+ '''
3543+# st_line = self.pool.get('account.bank.statement.line').browse(cr, uid, st_line_id, context=context)
3544+# if st_line.voucher_id:
3545+# res = self.pool.get('account.voucher').proforma_voucher(cr, uid, [st_line.voucher_id.id], context={'force_name': next_number})
3546+# return self.pool.get('account.move.line').write(cr, uid, [x.id for x in st_line.voucher_id.move_ids], {'statement_id': st_line.statement_id.id}, context=context)
3547+# return super(account_bank_statement, self).create_move_from_st_line(cr, uid, st_line, company_currency_id, next_number, context=context)
3548+
3549+ voucher_obj = self.pool.get('account.voucher')
3550+ wf_service = netsvc.LocalService("workflow")
3551+ move_line_obj = self.pool.get('account.move.line')
3552+ bank_st_line_obj = self.pool.get('account.bank.statement.line')
3553+ st_line = bank_st_line_obj.browse(cr, uid, st_line_id, context=context)
3554+ if st_line.voucher_id:
3555+ voucher_obj.write(cr, uid, [st_line.voucher_id.id], {'number': next_number}, context=context)
3556+ if st_line.voucher_id.state == 'cancel':
3557+ voucher_obj.action_cancel_draft(cr, uid, [st_line.voucher_id.id], context=context)
3558+ wf_service.trg_validate(uid, 'account.voucher', st_line.voucher_id.id, 'proforma_voucher', cr)
3559+
3560+ v = voucher_obj.browse(cr, uid, st_line.voucher_id.id, context=context)
3561+ bank_st_line_obj.write(cr, uid, [st_line_id], {
3562+ 'move_ids': [(4, v.move_id.id, False)]
3563+ })
3564+
3565+ return move_line_obj.write(cr, uid, [x.id for x in v.move_ids], {'statement_id': st_line.statement_id.id}, context=context)
3566+ return super(account_bank_statement, self).create_move_from_st_line(cr, uid, st_line.id, company_currency_id, next_number, context=context)
3567+
3568+account_bank_statement()
3569+
3570+class account_voucher_line_credits_to_use(osv.osv):
3571+ _name = "account.voucher.line.credits_to_use"
3572+ _rec_name = 'inv_credit'
3573+
3574+ def _credit_balance(self, cr, uid, ids, name, args, context=None):
3575+ '''
3576+ Function to calculate the value of variable Credit Balance
3577+ '''
3578+ res = {}
3579+ for line in self.browse(cr, uid, ids, context=context):
3580+ if line.use_credit:
3581+ res[line.id] = line.available_amount - line.discount_amount
3582+ else:
3583+ res[line.id] = line.available_amount
3584+ return res
3585+
3586+ _columns = {
3587+ 'voucher_line_id': fields.many2one('account.voucher.line', 'Account Voucher line', ondelete='cascade', readonly=True),
3588+ 'orginal_credit_line_id': fields.many2one('account.voucher.line', 'Account Voucher Credit Line', ondelete='cascade', readonly=True),
3589+ 'use_credit': fields.boolean('Use Credit',help='Used to indicate if credit should used against the invoice.', required=True),
3590+ 'inv_credit': fields.many2one('account.move.line', 'Invoice', help='Invoice of the credit', readonly=True),
3591+ 'discount_window_date': fields.date('Date',help='Date of the original credit', readonly=True),
3592+ 'orginal_amount': fields.float('Original Credit Amt', readonly=True),
3593+ 'available_amount': fields.float('Credit Amt Available', readonly=True),
3594+ 'discount_amount': fields.float('Amt to Use',help='Enter the amount of discount to be given.', required=True),
3595+ 'gl_account' : fields.many2one('account.account', 'G/L Account',help='Enter the General Ledger account number to record taking the cash discount.', required=True,readonly=True),
3596+ 'credit_bal': fields.function(_credit_balance, string='Credit Balance', method=True, type='float', store=False),
3597+ }
3598+ def onchage_use_credit(self, cr, uid, ids, use_credit, available_amount, amount_difference, context=None):
3599+ '''
3600+ Function to automatically fill or remove the discount amount when use credit checkbox checked or unchecked
3601+ '''
3602+ res = {}
3603+ if use_credit:
3604+ res['value'] = {'discount_amount': min(available_amount, amount_difference)}
3605+ else:
3606+ res['value'] = {'discount_amount': 0}
3607+ return res
3608+ def onchage_discount_amount(self, cr, uid, ids, available_amount, discount_amount, context=None):
3609+ '''
3610+ Function to validate the discount amount
3611+ '''
3612+ res = {}
3613+ if discount_amount < 0:
3614+ res['value'] = {'discount_amount': 0, 'use_credit':False }
3615+ res['warning'] = {'title': 'Credit not in the limit', 'message': 'Credit should not be a negative value.'}
3616+ return res
3617+ if discount_amount > available_amount:
3618+ res['value'] = {'discount_amount': available_amount, 'use_credit':True }
3619+ res['warning'] = {'title': 'Credit not in the limit', 'message': 'Please adjust the Credit Amt value to be less than or equal the Credit Available.'}
3620+ return res
3621+ if discount_amount == 0.0:
3622+ res['value'] = { 'use_credit':False }
3623+ else:
3624+ res['value'] = { 'use_credit':True }
3625+ return res
3626+account_voucher_line_credits_to_use()
3627+
3628+'''
3629+ Hook for account_voucher_writeoff_us
3630+'''
3631+class res_company(osv.osv):
3632+ '''
3633+ New field to keep the write-off account configuration on company
3634+ This Write-off account will be used on Account voucher.
3635+ '''
3636+ _name = 'res.company'
3637+ _inherit = 'res.company'
3638+ _columns = {
3639+ 'def_supp_journal': fields.many2one('account.journal','Default Supplier Payment Method',readonly=False),
3640+ 'writeoff_account': fields.many2one('account.account', 'Writeoff Account', domain=[('type','!=','view'),('type','!=','consolidation')],
3641+ help="This is the designated write-off gl account that will be used when writing off remaining amounts in customer payment."),
3642+ }
3643+res_company()
3644
3645=== added file 'account_voucher_credits_us/voucher_payment_receipt_view.xml'
3646--- account_voucher_credits_us/voucher_payment_receipt_view.xml 1970-01-01 00:00:00 +0000
3647+++ account_voucher_credits_us/voucher_payment_receipt_view.xml 2011-10-06 15:24:58 +0000
3648@@ -0,0 +1,220 @@
3649+<?xml version="1.0" encoding="UTF-8"?>
3650+<openerp>
3651+ <data>
3652+
3653+
3654+
3655+ <record model="ir.ui.view" id="view_vendor_receipt_form_1">
3656+ <field name="name">account.voucher.receipt.form.1</field>
3657+ <field name="model">account.voucher</field>
3658+ <field name="type">form</field>
3659+ <field name="inherit_id" ref="account_voucher.view_vendor_receipt_form"/>
3660+ <field name="arch" type="xml">
3661+ <xpath expr="/form/group/field[@name='amount']" position="attributes" >
3662+ <attribute name='on_change'></attribute>
3663+ </xpath>
3664+ <xpath expr="/form/group/field[@name='amount']" position="replace">
3665+ <field name="amount" string="Paid Amount" />
3666+ </xpath>
3667+ <xpath expr="/form/group[@col='10']/button[@name='cancel_voucher']" position="after">
3668+ <button name="calc_diff" string="Calculate" type="object" states="draft" icon="gtk-execute" />
3669+ </xpath>
3670+ <xpath expr="/form/notebook/page[@string='Payment Information']/group[@colspan='1']/group[@colspan='1']/field[@name='number']" position="after">
3671+ <field name="auto_match" />
3672+ </xpath>
3673+ <xpath expr="/form/notebook/page[@string='Payment Information']/field[@name='line_dr_ids']/tree" position="replace">
3674+ <tree string="Credits" editable="bottom">
3675+ <field name="move_line_id"/>
3676+ <field name="date_original" readonly="1"/>
3677+ <field name="amount_original" readonly="1"/>
3678+ <field name="amount" sum="Total Amt Used" string="Amt Used" />
3679+ <field name="pending_credits" sum="Total Pending Credit Used" readonly="1"/>
3680+ <field name="amount_unreconciled" string="Amt Available" readonly="1"/>
3681+ <field name="account_id" groups="base.group_extended" domain="[('type','=','receivable')]"/>
3682+ </tree>
3683+ <form string="Credits">
3684+ <field name="move_line_id" />
3685+ <field name="date_original" readonly="1"/>
3686+ <field name="amount_original" readonly="1"/>
3687+ <field name="amount" string="Amount Used" readonly="False"/>
3688+ <field name="pending_credits" readonly="1" sum="Total Credit Used"/>
3689+ <field name="amount_unreconciled" string="Amount Available" readonly="1"/>
3690+ <field name="account_id" groups="base.group_extended" domain="[('type','=','receivable')]"/>
3691+ </form>
3692+ </xpath>
3693+
3694+ <xpath expr="/form/notebook/page[@string='Payment Information']/field[@name='line_cr_ids']" position="replace">
3695+ <field name="line_cr_ids" default_get="{'journal_id':journal_id, 'type':type, 'partner_id':partner_id}" colspan="4" nolabel="1" height="140" string="Payment Modification">
3696+ <tree string="Invoices and Outstanding Transactions" editable="bottom">
3697+ <field name="move_line_id" context="{'journal_id':parent.journal_id, 'partner_id':parent.partner_id}"
3698+ on_change="onchange_move_line_id(move_line_id)"
3699+ domain="[('account_id.type','in',('receivable','payable')), ('reconcile_id','=', False), ('partner_id','=',parent.partner_id)]"
3700+ />
3701+ <field name="date_original" readonly="1"/>
3702+ <field name="date_due" readonly="1"/>
3703+ <field name="amount_original" readonly="1"/>
3704+ <field name="amount_unreconciled" sum="Open Balance" readonly="1"/>
3705+ <field name="pay" on_change="onchange_pay(amount, pay, amount_unreconciled, parent.line_cr_ids, parent.amount, credit_used, discount_used, writeoff_amount)"/>
3706+ <field name="amount" sum="Payment"/>
3707+ <field name="discount" />
3708+ <field name="amount_difference"/>
3709+ <field name="credit_used"/>
3710+ <field name="account_id"/>
3711+ <field name="discount_used" invisible="1"/>
3712+ <field name="writeoff_amount" invisible="1"/>
3713+ </tree>
3714+
3715+
3716+ <form >
3717+ <group string="Invoice" colspan="4" col="4" >
3718+ <group colspan="2" cols="2">
3719+ <field name="name" readonly="True"/>
3720+ <newline/>
3721+ <field name="invoice_id" readonly="True"/>
3722+ <newline/>
3723+ <field name="date_original" readonly="True"/>
3724+ </group>
3725+
3726+ <group color="red" colspan="2" cols="2" >
3727+ <field name="amount_original"/>
3728+ <newline/>
3729+ <field name="amount_unreconciled"/>
3730+ <newline/>
3731+ <field name='amount' />
3732+ <newline/>
3733+ <field name="credit_used"/>
3734+ <newline />
3735+ <field name="amount_difference"/>
3736+ </group>
3737+ </group>
3738+ <notebook tabpos="up" colspan="4" >
3739+ <page string="Credit">
3740+ <field name="available_credits" nolabel="1" colspan="4" string="Avilable Credits" view_mode="tree" >
3741+ </field>
3742+ </page>
3743+ <page string="Other Info" readonly="1">
3744+ <field name='account_id'/>
3745+ <field name='partner_id' readonly="1"/>
3746+ <field name='untax_amount' readonly="1"/>
3747+ <field name='type' readonly="1"/>
3748+ <field name='account_analytic_id' readonly="1"/>
3749+ <field name='date_due' readonly="1"/>
3750+ <field name='company_id' readonly="1"/>
3751+ <field name='pay' on_change="onchange_pay(amount, pay, amount_unreconciled, parent.line_cr_ids, parent.amount, credit_used, discount_used, writeoff_amount)"/>
3752+ </page>
3753+ </notebook>
3754+ <group colspan="1" cols="1">
3755+ <label/><label/>
3756+ <button name="clear_values" icon='gtk-clear' string="Clear" type="object" colspan="1"/>
3757+ <button name="recalculate_values" icon='gtk-refresh' string="Re-Calculate" type="object" colspan="1"/>
3758+ </group>
3759+ </form>
3760+
3761+ </field>
3762+ </xpath>
3763+
3764+ </field>
3765+ </record>
3766+
3767+
3768+ <record model="ir.ui.view" id="view_account_voucher_line_credits_to_use_tree">
3769+ <field name="name">account.voucher.line.credits_to_use.tree</field>
3770+ <field name="model">account.voucher.line.credits_to_use</field>
3771+ <field name="type">tree</field>
3772+ <field name="arch" type="xml">
3773+ <tree string="Available Credits" editable="top" >
3774+ <field name="use_credit" width="100" on_change="onchage_use_credit(use_credit, available_amount, parent.amount_difference)"/>
3775+ <field name="inv_credit"/>
3776+ <field name="discount_window_date"/>
3777+ <field name="orginal_amount"/>
3778+ <field name="available_amount"/>
3779+ <field name="discount_amount" on_change="onchage_discount_amount(available_amount, discount_amount)"/>
3780+ <field name="credit_bal"/>
3781+ <field name="gl_account"/>
3782+ </tree>
3783+ </field>
3784+ </record>
3785+
3786+ <record model="ir.ui.view" id="view_vendor_receipt_form_2">
3787+ <field name="name">account.voucher.receipt.form.2</field>
3788+ <field name="model">account.voucher</field>
3789+ <field name="type">form</field>
3790+ <field name="inherit_id" ref="account_voucher.view_vendor_receipt_form"/>
3791+ <field name="arch" type="xml">
3792+ <xpath expr="/form/notebook/page[@string='Payment Information']/field/tree/field[@name='move_line_id']" position="replace">
3793+ <field name="move_line_id" context="{'journal_id':parent.journal_id, 'partner_id':parent.partner_id}"
3794+ domain="[('account_id.type','in',('receivable','payable')), ('reconcile_id','=', False), ('partner_id','=',parent.partner_id)]"
3795+ />
3796+ </xpath>
3797+ </field>
3798+ </record>
3799+ <record model="ir.ui.view" id="view_vendor_receipt_form_3">
3800+ <field name="name">account.voucher.receipt.form.3</field>
3801+ <field name="model">account.voucher</field>
3802+ <field name="type">form</field>
3803+ <field name="inherit_id" ref="account_voucher.view_vendor_receipt_form"/>
3804+ <field name="arch" type="xml">
3805+ <xpath expr="/form/notebook/page[@string='Payment Information']/field/tree/field[@name='account_id']" position="replace">
3806+ <field name="account_id" groups="base.group_extended" domain="[('type','=','receivable')]"/>
3807+ </xpath>
3808+ </field>
3809+ </record>
3810+ <record model="ir.ui.view" id="view_vendor_receipt_form_4">
3811+ <field name="name">account.voucher.receipt.form.4</field>
3812+ <field name="model">account.voucher</field>
3813+ <field name="type">form</field>
3814+ <field name="inherit_id" ref="account_voucher.view_vendor_receipt_form"/>
3815+ <field name="arch" type="xml">
3816+ <xpath expr="/form/notebook/page[@string='Payment Information']/field/tree/field[@name='amount']" position="replace">
3817+ <field name="amount" sum="Payment" />
3818+ </xpath>
3819+ </field>
3820+ </record>
3821+
3822+ <record id="action_proforma_voucher" model="ir.actions.act_window">
3823+ <field name="name">Post Voucher</field>
3824+ <field name="type">ir.actions.act_window</field>
3825+ <field name="res_model">account.post.voucher</field>
3826+ <field name="view_type">form</field>
3827+ <field name="view_mode">form</field>
3828+ <field name="target">new</field>
3829+ </record>
3830+ <record model="ir.ui.view" id="view_vendor_receipt_form_5">
3831+ <field name="name">account.voucher.receipt.form.5</field>
3832+ <field name="model">account.voucher</field>
3833+ <field name="type">form</field>
3834+ <field name="inherit_id" ref="account_voucher.view_vendor_receipt_form"/>
3835+ <field name="arch" type="xml">
3836+ <xpath expr="/form/group[@col='10']/button[@name='proforma_voucher']" position="replace">
3837+ <button name="%(action_proforma_voucher)d" string="Post" states="draft" type="action" attrs="{'invisible': ['|',('cc_info_hide','=',False),('state','&lt;&gt;','draft')]}" icon="terp-camera_test"/>
3838+ </xpath>
3839+ </field>
3840+ </record>
3841+ <!--
3842+ Company
3843+ -->
3844+ <record id="view_company_form_jdc3" model="ir.ui.view">
3845+ <field name="name">res.company.form.jdc3</field>
3846+ <field name="model">res.company</field>
3847+ <field name="type">form</field>
3848+ <field name="inherit_id" ref="base.view_company_form"/>
3849+ <field name="priority" eval="1"/>
3850+ <field name="arch" type="xml">
3851+ <xpath expr="/form/notebook/page[@string='Configuration']" position="inside">
3852+ <group>
3853+ <separator string="Supplier Payment" colspan="2"/><newline/>
3854+ <field name="def_supp_journal" domain="[('type','in',['bank','cash'])]"/>
3855+ </group>
3856+ </xpath>
3857+
3858+ <xpath expr="/form/notebook" position="inside">
3859+ <page string="Accounting">
3860+ <field name="writeoff_account" colspan="2"/>
3861+ </page>
3862+ </xpath>
3863+ </field>
3864+ </record>
3865+
3866+
3867+ </data>
3868+</openerp>
3869
3870=== added directory 'account_voucher_credits_us/wizard'
3871=== added file 'account_voucher_credits_us/wizard/__init__.py'
3872--- account_voucher_credits_us/wizard/__init__.py 1970-01-01 00:00:00 +0000
3873+++ account_voucher_credits_us/wizard/__init__.py 2011-10-06 15:24:58 +0000
3874@@ -0,0 +1,26 @@
3875+# -*- coding: utf-8 -*-
3876+##############################################################################
3877+#
3878+# OpenERP, Open Source Management Solution
3879+# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
3880+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
3881+#
3882+# This program is free software: you can redistribute it and/or modify
3883+# it under the terms of the GNU General Public License as published by
3884+# the Free Software Foundation, either version 3 of the License, or
3885+# (at your option) any later version.
3886+#
3887+# This program is distributed in the hope that it will be useful,
3888+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3889+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3890+# GNU General Public License for more details.
3891+#
3892+# You should have received a copy of the GNU General Public License
3893+# along with this program. If not, see <http://www.gnu.org/licenses/>
3894+#
3895+##############################################################################
3896+
3897+import account_post_voucher
3898+
3899+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
3900+
3901
3902=== added file 'account_voucher_credits_us/wizard/account_post_voucher.py'
3903--- account_voucher_credits_us/wizard/account_post_voucher.py 1970-01-01 00:00:00 +0000
3904+++ account_voucher_credits_us/wizard/account_post_voucher.py 2011-10-06 15:24:58 +0000
3905@@ -0,0 +1,75 @@
3906+# -*- coding: utf-8 -*-
3907+##############################################################################
3908+#
3909+# OpenERP, Open Source Management Solution
3910+# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
3911+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
3912+#
3913+# This program is free software: you can redistribute it and/or modify
3914+# it under the terms of the GNU General Public License as published by
3915+# the Free Software Foundation, either version 3 of the License, or
3916+# (at your option) any later version.
3917+#
3918+# This program is distributed in the hope that it will be useful,
3919+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3920+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3921+# GNU General Public License for more details.
3922+#
3923+# You should have received a copy of the GNU General Public License
3924+# along with this program. If not, see <http://www.gnu.org/licenses/>
3925+#
3926+##############################################################################
3927+
3928+from osv import fields, osv
3929+
3930+class account_post_voucher(osv.osv_memory):
3931+ _name = 'account.post.voucher'
3932+ _description = 'Account Pay Voucher'
3933+
3934+ _columns = {
3935+ 'total_paid': fields.float('Total Received'),
3936+ 'total_allocated': fields.float('Total Allocated'),
3937+ 'ok_to_go': fields.float('OK to Go'),
3938+ }
3939+
3940+ def _get_total_paid(self, cr, uid, context={}):
3941+ obj_voucher = self.pool.get('account.voucher')
3942+ return obj_voucher.browse(cr, uid, context['active_id'], context).amount
3943+
3944+ def _get_total_allocated(self, cr, uid, context={}):
3945+ obj_voucher = self.pool.get('account.voucher')
3946+ voucher = obj_voucher.browse(cr, uid, context['active_id'], context)
3947+ total_allocated = 0.0
3948+ for line in voucher.line_cr_ids:
3949+ total_allocated += line.amount
3950+ return total_allocated
3951+
3952+ def _get_ok_to_go(self,cr, uid, context={}):
3953+ obj_voucher = self.pool.get('account.voucher')
3954+ voucher = obj_voucher.browse(cr, uid, context['active_id'], context)
3955+ total_allocated = 0.0
3956+ for line in voucher.line_cr_ids:
3957+ total_allocated += line.amount
3958+ return total_allocated - voucher.amount
3959+
3960+ _defaults = {
3961+ 'total_paid': _get_total_paid,
3962+ 'total_allocated': _get_total_allocated,
3963+ 'ok_to_go': _get_ok_to_go,
3964+ }
3965+ def onchange_ok_to_go(self,cr, uid, ids, ok_to_go, context={}):
3966+ if ok_to_go > 0.0:
3967+ return {'warning': {'title': 'Overallocated invoices', 'message': 'Reduce allocations to match Total Receipt'}}
3968+ else:
3969+ return {'value':{}}
3970+ def launch_wizard(self, cr, uid, ids, context=None):
3971+ """
3972+ Don't allow post if total_allocated > total_paid.
3973+ """
3974+ obj_voucher = self.pool.get('account.voucher')
3975+ obj_voucher.action_move_line_create(cr, uid, context['active_ids'], context)
3976+ return {}
3977+
3978+account_post_voucher()
3979+
3980+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
3981
3982=== added file 'account_voucher_credits_us/wizard/account_post_voucher.xml'
3983--- account_voucher_credits_us/wizard/account_post_voucher.xml 1970-01-01 00:00:00 +0000
3984+++ account_voucher_credits_us/wizard/account_post_voucher.xml 2011-10-06 15:24:58 +0000
3985@@ -0,0 +1,40 @@
3986+<?xml version="1.0" encoding="utf-8"?>
3987+<openerp>
3988+ <data>
3989+
3990+ <record id="account_voucher_post_view" model="ir.ui.view">
3991+ <field name="name">account.post.voucher.form</field>
3992+ <field name="model">account.post.voucher</field>
3993+ <field name="type">form</field>
3994+ <field name="arch" type="xml">
3995+ <form string="Post Voucher">
3996+ <group colspan="2" col="4">
3997+ <field name="total_paid" readonly="True"/>
3998+ <field name="total_allocated" readonly="True"/>
3999+ <field name="ok_to_go" invisible="True" on_change="onchange_ok_to_go(ok_to_go)"/>
4000+ </group>
4001+ <group height="100" width="320">
4002+ <separator string="Confirm Amounts?"/>
4003+ <newline/>
4004+ <group colspan="2" col="4">
4005+ <button special="cancel" string="Cancel" icon="gtk-cancel"/>
4006+ <button name="launch_wizard" string="Yes" type="object" attrs="{'invisible':[('ok_to_go', '>', 0.0)]}" icon="gtk-ok" default_focus="1"/>
4007+ </group>
4008+ </group>
4009+ </form>
4010+ </field>
4011+ </record>
4012+
4013+ <record id="action_proforma_voucher" model="ir.actions.act_window">
4014+ <field name="name">Post Voucher</field>
4015+ <field name="type">ir.actions.act_window</field>
4016+ <field name="res_model">account.post.voucher</field>
4017+ <field name="view_type">form</field>
4018+ <field name="view_mode">form</field>
4019+ <field name="view_id" ref="account_voucher_post_view"/>
4020+ <field name="target">new</field>
4021+ </record>
4022+
4023+
4024+ </data>
4025+</openerp>

Subscribers

People subscribed via source and target branches

to all changes: