1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# OpenERP, Open Source Management Solution
5
# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
6
# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
8
# This program is free software: you can redistribute it and/or modify
9
# it under the terms of the GNU General Public License as published by
10
# the Free Software Foundation, either version 3 of the License, or
11
# (at your option) any later version.
13
# This program is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
# GNU General Public License for more details.
18
# You should have received a copy of the GNU General Public License
19
# along with this program. If not, see <http://www.gnu.org/licenses/>
21
##############################################################################
23
from collections import defaultdict
25
from mx.DateTime import RelativeDateTime
27
from osv import fields, osv
28
import decimal_precision as dp
29
from tools.amount_to_text_en import amount_to_text
30
from amount_to_words import amount_to_words
32
def append_to_list(item, main_list):
34
@param item: the item to be added to the list. Can be an integer or list
35
@param main_list: The list to which the item needs to be added.
36
@return: The new list formed by appending the item to the list
38
if not isinstance(item, list):
40
return main_list + item
42
class account_payment_term(osv.osv):
43
_inherit = "account.payment.term"
45
'cash_discount_ids': fields.one2many('account.cash.discount', 'payment_id', 'Cash Discounts'),
48
def get_discounts(self, cr, uid, id, base_date, context=None):
50
return the list of (date,percentage) ordered by date for the
51
payment term with the corresponding id. return [] if no cash
52
discount are defined. base_date is the date from where the
53
discounts are computed.
56
for pt in self.browse(cr, uid, id, context=context):
57
for d in pt.cash_discount_ids:
59
((mx.DateTime.strptime(base_date, '%Y-%m-%d') +\
60
RelativeDateTime(days=d.delay)).strftime("%Y-%m-%d"),
63
res.sort(cmp=lambda x, y: cmp(x[0], y[0]))
66
account_payment_term()
69
class account_invoice(osv.osv):
71
Add discount calculation to invoice
73
_inherit = 'account.invoice'
75
def _get_discount(self, cr, uid, ids, field_name, args, context=None):
77
Calculate the value of variable date_discount (Discount Date) and amount_discounted (Discounted Total)
80
for invoice in self.browse(cr, uid, ids, context=context):
81
res = defaultdict(list)
83
'date_discount': invoice.date_due,
84
'amount_discounted': invoice.amount_total
86
if not invoice.date_invoice:
87
invoice_date = mx.DateTime.today().strftime("%Y-%m-%d")
88
self.write(cr, uid, [invoice.id], {'date_invoice': invoice_date}, context=context)
90
invoice_date = invoice.date_invoice
91
discounts = invoice.payment_term and invoice.payment_term.get_discounts(invoice_date, context=context)
94
non_discount_total = 0.0
95
for line in invoice.invoice_line:
96
if line.cash_discount:
97
discount_total += line.price_subtotal
98
line_cash_discount = round((1.0 - discounts[0][1]) * line.price_subtotal)
100
non_discount_total += line.price_subtotal
101
line_cash_discount = 0.0
102
self.pool.get('account.invoice.line').write(cr, uid, line.id, {'cash_discount': line_cash_discount}, context=context)
103
# assume taxes are never discountable
104
non_discount_total += invoice.amount_tax
105
# There may be more than one - return the earliest
107
'date_discount': discounts[0][0],
108
'amount_discounted': round(((1.0 - discounts[0][1]) * discount_total) + non_discount_total, 2)
113
'date_discount': fields.function(_get_discount, method=True, type='date', string='Discount Date', multi='all'),
114
'amount_discounted': fields.function(_get_discount, method=True, type='float', digits_compute=dp.get_precision('Account'),
115
string='Discounted Total', multi='all'),
120
class account_voucher(osv.osv):
121
_inherit = 'account.voucher'
123
def calc_supp_diff(self, cr, uid, ids, context=None):
125
Called by calculate/re-calculate action.
126
This method will update the credit lines on voucher lines.
127
If the field "auto_match" marked, this method will run a matching routine
129
for vch in self.browse(cr, uid, ids, context=context):
130
for line in vch.line_dr_ids:
131
# Update the credit lines and discount lines so that matching routine can use the latest available credits and discounts
132
line._update_supp_discount_lines(context=context)
133
for vch in self.browse(cr, uid, ids, context=context):
135
for line in vch.line_dr_ids:
137
amount += float(line.amount)
139
self.write(cr, uid, [vch.id], {'amount': amount}, context=context)
140
return {'nodestroy': True}
142
def _update_discounts(self, lines, vch_date):
143
date_discount = False
144
amount_discount = False
146
if 'date_discount' in line:
147
date_discount = line['date_discount']
148
amount_discounted = line['amount_discounted']
151
if line['amount'] >= line['amount_unreconciled']:
152
amount_discount = 0.0
153
elif vch_date <= date_discount and line['amount'] <= line['amount_unreconciled'] and line['amount'] >= amount_discounted:
154
amount_discount = max(line['amount_unreconciled'] - amount_discounted, 0.0)
155
elif vch_date <= date_discount and line['amount'] < amount_discounted:
156
amount_discount = max(line['amount_unreconciled'] - amount_discounted, 0.0)
157
line['cash_discount'] = amount_discount
158
line['amount_difference'] = line['amount_unreconciled'] - line['amount'] - amount_discount
160
def onchange_partner_id(self, cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, date, context=None):
162
Function to update fields in customer payment form on changing customer
164
currency_pool = self.pool.get('res.currency')
165
journal_pool = self.pool.get('account.journal')
166
invoice_pool = self.pool.get('account.invoice')
167
line_pool = self.pool.get('account.voucher.line')
168
partner_pool = self.pool.get('res.partner')
169
company_currency = False
170
default = super(account_voucher, self).onchange_partner_id(cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, date, context=context)
173
# we have to clear out lines, because new lines will be created by the change
175
partner = partner_pool.browse(cr, uid, partner_id, context=context)
176
# FIXME: Possibly unwanted code. Cannot find field payment_meth_id.
177
if hasattr(partner, 'payment_meth_id') and partner.payment_meth_id:
178
payment_mode_pool = self.pool.get('payment.mode')
179
payment_meth = payment_mode_pool.browse(cr, uid, partner.payment_meth_id.id, context=context)
181
default['value']['journal_id'] = payment_meth.journal.id
182
journal_id = payment_meth.journal.id
183
line_ids = line_pool.search(cr, uid, [('voucher_id', 'in', ids)], context=context)
185
line_pool.unlink(cr, uid, line_ids, context=context)
187
journal = journal_pool.browse(cr, uid, journal_id, context=context)
188
company_currency = journal and journal.company_id and journal.company_id.currency_id and journal.company_id.currency_id.id
192
for vch in self.browse(cr, uid, ids, context=context):
194
if default and 'value' in default and 'line_cr_ids' in default['value']:
195
for line in default['value']['line_cr_ids']:
196
invoice_id = invoice_pool.search(cr, uid, [('number', '=', line['name'])], context=context)
198
line['invoice_id'] = invoice_id[0]
199
invoice = invoice_pool.browse(cr, uid, invoice_id[0], context=context)
200
date_discount = invoice.date_discount
201
amount_discounted = invoice.amount_discounted
202
line['date_discount'] = date_discount
203
line['amount_discounted'] = amount_discounted
205
line['date_discount'] = False
206
line['amount_discounted'] = 0.0
208
total_credit += line['type'] == 'cr' and line['amount_unreconciled'] or 0.0
209
total_debit += line['type'] == 'dr' and line['amount_unreconciled'] or 0.0
210
# first, see if we can find an invoice matching the amount to be applied
213
def calc_amount(line, total):
214
return min(line['amount_unreconciled'], total)
216
lines = default['value']['line_cr_ids']
220
# if only one, assign it
223
lines[0]['amount'] = currency_pool.compute(cr, uid, company_currency, currency_id, amount) or amount
224
if lines[0]['type'] == 'cr':
225
total_credit -= amount
227
total_debit -= amount
229
self._update_discounts(lines, vch_date)
232
if line['amount_unreconciled'] == price:
233
if line['type'] == 'cr':
234
amount = calc_amount(line, total_credit)
235
line['amount'] = currency_pool.compute(cr, uid, company_currency, currency_id, amount) or amount
236
total_credit -= amount
240
amount = calc_amount(line, total_debit)
241
line['amount'] = currency_pool.compute(cr, uid, company_currency, currency_id, amount) or amount
242
total_debit -= amount
246
# see if we can find a combination that matches
247
def search(lines, price):
248
for i in range(len(lines)):
249
if lines[i]['amount_unreconciled'] == price:
251
for i in range(len(lines)):
252
for j in range(i + 1, len(lines)):
253
if lines[i]['amount_unreconciled'] + lines[j]['amount_unreconciled'] == price:
254
return [lines[i], lines[j]]
255
for i in range(len(lines)):
256
for j in range(i + 1, len(lines)):
257
for k in range(j + 1, len(lines)):
258
if lines[i]['amount_unreconciled'] + lines[j]['amount_unreconciled'] + lines[k]['amount_unreconciled'] == price:
259
return [lines[i], lines[j], lines[k]]
261
line_ids = search(lines, price)
264
for line in line_ids:
265
if line['type'] == 'cr':
266
amount = calc_amount(line, line['amount_unreconciled'])
267
line['amount'] = currency_pool.compute(cr, uid, company_currency, currency_id, amount)
268
total_debit -= amount
271
amount = calc_amount(line, line['amount_unreconciled'])
272
line['amount'] = currency_pool.compute(cr, uid, company_currency, currency_id, amount)
273
total_credit -= amount
276
# see if we can find a match using discounted amount
277
def search2(lines, price):
278
for i in range(len(lines)):
279
if min(lines[i]['amount_unreconciled'], lines[i]['amount_discounted']) == price:
281
for i in range(len(lines)):
282
for j in range(i + 1, len(lines)):
283
if min(lines[i]['amount_unreconciled'], lines[i]['amount_discounted']) + min(lines[j]['amount_unreconciled'],
284
lines[j]['amount_discounted']) == price:
285
return [lines[i], lines[j]]
286
for i in range(len(lines)):
287
for j in range(i + 1, len(lines)):
288
for k in range(j + 1, len(lines)):
289
if min(lines[i]['amount_unreconciled'], lines[i]['amount_discounted']) + min(lines[j]['amount_unreconciled'],
290
lines[j]['amount_discounted']) + min(lines[k]['amount_unreconciled'], lines[k]['amount_discounted']) == price:
291
return [lines[i], lines[j], lines[k]]
292
#FIXME: Is this call really necessary?
293
line_ids = search(lines, price)
294
lines = default['value']['line_cr_ids']
295
line_ids = search2(lines, price)
298
for line in line_ids:
299
amount = calc_amount(line, min(line['amount_unreconciled'], line['amount_discounted']))
300
if line['type'] == 'cr':
301
total_debit -= amount
303
total_credit -= amount
304
line['amount'] = currency_pool.compute(cr, uid, company_currency, currency_id,
305
min(line['amount_unreconciled'], line['amount_discounted']))
306
lines = default['value']['line_cr_ids']
308
# FIXME: removing amount from line_dr_ids (Credits on customer payment form) line.
309
# and amount from line_cr_ids (Invoice and outstanding transactions on customer payment form) line
310
# I do not think this this is a good solution. But it works.
311
# The whole "onchange_partner_id" function need a re-thinking (may have to rewrite it completely instead of calling super ).
314
for credit_line in default['value'].get('line_dr_ids', []):
315
credit_line['amount'] = 0.0
316
for invoce_line in default['value'].get('line_cr_ids', []):
317
invoce_line['amount'] = 0.0
318
invoce_line['amount_difference'] = invoce_line['amount_unreconciled']
321
def calc_cash_discount(self, cr, uid, ids, vch, line, context=None):
323
Calculate discount per line
327
total_allocated = 0.0
328
for line in vch.line_ids:
329
total_allocated += line.amount
330
context.update({'total_allocated': total_allocated, 'total_amount': vch.date})
331
amount_discount = 0.0
332
if line.amount >= line.amount_unreconciled or line.amount < 0.01:
333
amount_discount = 0.0
334
elif line.amount >= line.amount_discounted and vch.date <= line.date_discount:
335
amount_discount = line.amount_unreconciled - line.amount_discounted
336
return amount_discount
338
def onchange_amount(self, cr, uid, ids, amount, context=None):
340
Function to convert amount to words
343
currency_format = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_format
344
if currency_format == 'us':
345
amount_in_words = amount_to_words(amount)
347
amount_in_words = amount_to_text(amount)
348
result['amount_in_word'] = amount_in_words
349
return {'value': result}
353
class account_voucher_line(osv.osv):
354
_inherit = 'account.voucher.line'
356
def onchange_supp_pay(self, cr, uid, ids, line_amount, pay, amount_unreconciled, par_cr_ids, par_amount, credit_used, discount_used,
357
writeoff_amount=0, context=None):
359
Function to automatically fill the values when the pay checkbox is selected
362
# FIXME: Please verify the following two implementation.
363
# writeoff_amount = (not writeoff_amount and [0] or [writeoff_amount])[0]
364
# discount_used = (not discount_used and [0] or [discount_used])[0]
365
# credit_used = (not credit_used and [0] or [credit_used])[0]
366
writeoff_amount = writeoff_amount or 0
367
discount_used = discount_used or 0
368
credit_used = credit_used or 0
371
tot_amt = par_amount + line_amount
372
for credit in par_cr_ids:
373
if 'pay' in credit[2] and 'amount' in credit[2]:
374
tot_amt -= (credit[2]['amount'])
378
amount_unreconciled -= (discount_used + writeoff_amount + credit_used)
379
# FIXME: Please verify the following two operations
380
# ret['amount'] = min(tot_amt, (amount_unreconciled < 0) and 0 or amount_unreconciled)
381
ret['amount'] = min(tot_amt, amount_unreconciled)
384
return {'value': ret}
386
def recalculate_supp_values(self, cr, uid, ids, context=None):
388
Re-calculate button action
390
if isinstance(ids, list):
392
voucher_line = self.browse(cr, uid, ids, context=context)
393
if voucher_line.discount_used:
394
self.write(cr, uid, ids, {'amount': voucher_line.amount_unreconciled - voucher_line.discount_used}, context=context)
395
self.pool.get('account.voucher').calc_supp_diff(cr, uid, [voucher_line.voucher_id.id], context=context)
398
def _update_credit_lines(self, cr, uid, ids, context=None):
400
Function to update the credit lines in payment lines
402
credits_used_pool = self.pool.get('account.voucher.line.credits_to_use')
403
for line in self.browse(cr, uid, ids, context=context):
404
credits_lines_used = [x.orginal_credit_line_id.id for x in line.available_credits]
405
for credit_line in line.voucher_id.line_dr_ids:
406
if credit_line.id not in credits_lines_used and line.invoice_id:
407
credits_used_pool.create(cr, uid, {
408
'voucher_line_id': line.id,
409
'orginal_credit_line_id': credit_line.id,
411
'inv_credit': credit_line.move_line_id.id,
412
'discount_window_date': credit_line.date_original,
413
'orginal_amount': credit_line.amount_original,
414
'available_amount': credit_line.amount_unreconciled - credit_line.pending_credits,
415
'discount_amount': 0.0,
416
'gl_account': credit_line.account_id.id,
419
to_update_credit_line_ids = credits_used_pool.search(cr, uid, [('voucher_line_id', '=', line.id),
420
('orginal_credit_line_id', '=', credit_line.id)], context=context)
421
credits_used_pool.write(cr, uid, to_update_credit_line_ids,
422
{'available_amount': credit_line.amount_unreconciled - credit_line.pending_credits}, context=context)
424
def _update_discount_lines(self, cr, uid, ids, context=None):
426
Function to update the discount lines in payment lines
428
discount_used_pool = self.pool.get('account.voucher.line.discount_to_use')
429
user_pool = self.pool.get('res.users')
430
user = user_pool.browse(cr, uid, uid, context=context)
431
for line in self.browse(cr, uid, ids, context=context):
433
if not line.invoice_id.date_discount or not line.voucher_id.date or line.voucher_id.date > line.invoice_id.date_discount:
434
#customer is not eligible for the discount
437
discount = line.invoice_id.amount_total - line.invoice_id.amount_discounted
438
date_discount = line.invoice_id.date_discount
439
discount_found = False
440
for discount_line in line.available_discounts:
441
if line.invoice_id.payment_term.id == discount_line.inv_payment_terms.id:
442
discount_found = True
444
if not discount_found and user.company_id.sales_discount_account:
445
discount_used_pool.create(cr, uid, {
446
'voucher_line_id': line.id,
447
'use_discount': False,
448
'inv_payment_terms': line.invoice_id.payment_term.id,
449
'discount_window_date': date_discount,
450
'proposed_discount': discount,
451
'discount_amount': 0.0,
452
'gl_account': user.company_id.sales_discount_account.id
455
def _update_supp_discount_lines(self, cr, uid, ids, context=None):
457
Function to update the discount lines in payment lines
459
discount_used_pool = self.pool.get('account.voucher.line.discount_to_use')
460
user_pool = self.pool.get('res.users')
461
user = user_pool.browse(cr, uid, uid, context=context)
462
for line in self.browse(cr, uid, ids, context=context):
464
if not line.invoice_id.date_discount or not line.voucher_id.date or line.voucher_id.date > line.invoice_id.date_discount:
465
#customer is not eligible for the discount
468
discount = line.invoice_id.amount_total - line.invoice_id.amount_discounted
469
date_discount = line.invoice_id.date_discount
470
discount_found = False
471
for discount_line in line.available_discounts:
472
if line.invoice_id.payment_term.id == discount_line.inv_payment_terms.id:
473
discount_found = True
475
if not discount_found and user.company_id.purchase_discount_account:
476
discount_used_pool.create(cr, uid, {
477
'voucher_line_id': line.id,
478
'use_discount': False,
479
'inv_payment_terms': line.invoice_id.payment_term.id,
480
'discount_window_date': date_discount,
481
'proposed_discount': discount,
482
'discount_amount': 0.0,
483
'gl_account':user.company_id.purchase_discount_account.id
485
for line in self.browse(cr, uid, ids, context=context):
487
for discount_line in line.available_discounts:
488
if not discount_line.credit_selected:
489
discount_used_pool.write(cr, uid, discount_line.id, {'use_discount': True, 'discount_amount': discount_line.proposed_discount},
491
self.write(cr, uid, line.id, {'amount': line.amount_original - line.discount_used}, context=context)
493
def _compute_discount_used(self, cr, uid, ids, name, args, context=None):
495
Function to calculate the value of variable discount used
498
for line in self.browse(cr, uid, ids, context=context):
500
for discount_line in line.available_discounts:
501
if discount_line.use_discount:
502
res[line.id] += discount_line.discount_amount
505
def _get_disc(self, cr, uid, ids, mode='cust', context=None):
507
@param cr: current row of the database
508
@param uid: id of the user currently logged in
509
@param ids: ids of the selected records
510
@param mode: cust or supp
511
@param context: context
514
invoice_obj = self.pool.get('account.invoice')
515
voucher_line = self.browse(cr, uid, ids, context=context)
516
line_type_map = {'cust': 'dr', 'supp': 'cr'}
518
res = defaultdict(list)
519
for line in voucher_line:
520
if not line.move_line_id:
522
date_discount = False
523
if line.type == line_type_map[mode]:
526
'amount_discounted': line.amount,
527
'cash_discount': 0.0,
529
if line.type == 'cust':
530
res[line.id].update({
531
'amount_difference': line.amount_unreconciled - line.amount
534
res[line.id].update({
535
'supp_amount_difference': line.amount_unreconciled - line.amount,
539
invoice_number = line.move_line_id.name or line.move_line_id.ref
541
if invoice_number and invoice_number != '/':
542
invoice_ids = invoice_obj.search(cr, uid, [('number', '=', str(invoice_number))], context=context)
544
invoice_number = line.move_line_id.move_id and line.move_line_id.move_id.name or line.move_line_id.move_id.ref
545
if invoice_number and invoice_number != '/':
546
invoice_ids = invoice_obj.search(cr, uid, [('number', '=', str(invoice_number))], context=context)
547
for invoice in invoice_obj.browse(cr, uid, invoice_ids, context=context):
548
date_discount = invoice.date_due
549
amount_discounted = invoice.amount_total
552
'date_discount': invoice.date_due,
553
'amount_discounted': invoice.amount_total
557
'date_discount': invoice.date_due,
558
'supp_amount_difference': invoice.amount_total
560
if not invoice.date_invoice:
561
invoice_date = mx.DateTime.today().strftime("%Y-%m-%d")
562
self.write(cr, uid, [invoice.id], {'date_invoice': invoice_date}, context=context)
564
invoice_date = invoice.date_invoice
565
discounts = invoice.payment_term and invoice.payment_term.get_discounts(invoice_date, context=context)
567
line_obj = self.pool.get('account.invoice.line')
569
non_discount_total = 0.0
570
for invline in invoice.invoice_line:
571
if invline.cash_discount:
572
discount_total += invline.price_subtotal
573
line_cash_discount = round((1.0 - discounts[0][1]) * invline.price_subtotal)
575
non_discount_total += invline.price_subtotal
576
line_cash_discount = 0.0
577
line_obj.write(cr, uid, invline.id, {'cash_discount': line_cash_discount}, context=context)
578
# assume taxes are never discountable
579
non_discount_total += invoice.amount_tax
580
# There may be more than one - return the earliest
581
date_discount = discounts[0][0]
582
amount_discounted = round(((1.0 - discounts[0][1]) * discount_total) + non_discount_total, 2)
583
amount_discount = 0.0
584
if line.amount >= line.amount_unreconciled or line.amount < 0.01:
585
amount_discount = 0.0
586
elif line.voucher_id.date <= date_discount and line.amount <= line.amount_unreconciled:
587
amount_discount = max(line.amount_unreconciled - amount_discounted, 0.0)
588
elif line.voucher_id.date <= date_discount and line.amount < amount_discounted:
589
amount_discount = max(line.amount_unreconciled - amount_discounted, 0.0)
591
amount_discount = 0.0
594
'date_discount': date_discount,
595
'amount_discounted': amount_discounted,
596
'cash_discount': amount_discount,
599
res[line.id].update({
600
'amount_difference': line.amount_unreconciled - line.amount - line.credit_used - line.discount_used - line.writeoff_amount
604
if line.available_discounts:
606
res[line.id].update({
607
'supp_amount_difference': line.amount_unreconciled - line.amount - line.credit_used - line.discount_used - line.writeoff_amount,
612
def _get_discount(self, cr, uid, ids, field_name, args, context=None):
614
Function to calculate the value of variable date_discount,amount_discounted,cash_discount,amount_difference
615
return the values as dictionary
617
return self._get_disc(cr, uid, ids, mode='cust', context=context)
619
def _get_supp_discount(self, cr, uid, ids, field_name, args, context=None):
621
Function to calculate the value of variable date_discount,amount_discounted,cash_discount,amount_difference
622
return the values as dictionary
624
return self._get_disc(cr, uid, ids, mode='supp', context=context)
627
'date_discount': fields.function(_get_discount, method=True, type='date', string='Discount Date',
629
'amount_discounted': fields.function(_get_discount, method=True, type='float', digits_compute=dp.get_precision('Account'),
630
string='Discounted Total', multi='all'),
631
'cash_discount': fields.function(_get_discount, method=True, type='float', multi="all", digits_compute=dp.get_precision('Account'),
632
string='Cash Discount', sequence=20),
633
'amount_difference': fields.function(_get_discount, method=True, multi='all', type='float', string='Unpaid Amt', digits=(16, 2)),
634
'supp_amount_difference': fields.function(_get_supp_discount, method=True, multi='all1', type='float', string='Unpaid Amt', digits=(16, 2)),
635
'interest': fields.float(string='Interest', digits=(16,2)),
637
'discount_used': fields.function(_compute_discount_used, method=True, type='float', string='Discount Used', store=False, readonly=True,
639
'available_discounts':fields.one2many('account.voucher.line.discount_to_use', 'voucher_line_id', 'Available Discounts'),
640
'discount': fields.function(_get_supp_discount, method=True, multi='all1', type='boolean', string='Discount', readonly=True, sequence=20)
643
def clear_values(self, cr, uid, ids, context=None):
645
Clear the selected credits, discounts and writeoffs from voucher line
647
voucher_line = self.browse(cr, uid, ids[0], context=context)
649
available_credits_ids = []
650
available_discounts_ids = []
651
if hasattr(voucher_line, 'writeoff_ids'):
652
for lines in self.read(cr, uid, ids, ['available_credits', 'writeoff_ids', 'available_discounts'], context=context):
653
writeoff_ids = append_to_list(lines['writeoff_ids'], writeoff_ids)
654
available_credits_ids = append_to_list(lines['available_credits'], available_credits_ids)
655
available_discounts_ids = append_to_list(lines['available_discounts'], available_discounts_ids)
657
for lines in self.read(cr, uid, ids, ['available_credits', 'available_discounts'], context=context):
658
available_credits_ids = append_to_list(lines['available_credits'], available_credits_ids)
659
available_discounts_ids = append_to_list(lines['available_discounts'], available_discounts_ids)
661
self.pool.get('account.voucher.line.writeoff').unlink(cr, uid, writeoff_ids, context=context)
662
self.pool.get('account.voucher.line.credits_to_use').write(cr, uid, available_credits_ids, {
664
'discount_amount':0.0
666
self.pool.get('account.voucher.line.discount_to_use').write(cr, uid, available_discounts_ids, {
667
'use_discount':False,
668
'discount_amount':0.0
672
account_voucher_line()
674
class product_product(osv.osv):
676
Add new account configuration fields to product
678
_inherit = 'product.product'
680
'cash_discount': fields.boolean('Cash Discount?'),
681
'purchase_discount_account': fields.many2one('account.account', 'Purchase Discount Account'),
682
'sales_discount_account': fields.many2one('account.account', 'Sales Discount Account', domain=[('type', '!=', 'view'),
683
('type', '!=', 'consolidation')]),
684
'purchase_discount_journal': fields.many2one('account.journal', 'Purchase Discount Journal', domain=[('type', '!=', 'view'),
685
('type', '!=', 'consolidation'),
686
('type', '=', 'purchase')]),
687
'sales_discount_journal': fields.many2one('account.journal', 'Sales Discount Journal', domain=[('type', '=', 'sale')]),
691
'cash_discount': True,
697
class account_invoice_line(osv.osv):
699
option to disable discount calculation per invoice line
701
_inherit = 'account.invoice.line'
703
'cash_discount': fields.boolean('Cash Discount?'),
706
'cash_discount': True,
709
def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False,
710
address_invoice_id=False, currency_id=False, context=None):
712
check if the discount is applicable to newly selected product
714
result = super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom, qty=qty, name=name, type=type, partner_id=partner_id, fposition_id=fposition_id, price_unit=price_unit,
715
address_invoice_id=address_invoice_id, currency_id=currency_id, context=context)
717
res = self.pool.get('product.product').read(cr, uid, product, ['cash_discount'], context=context)
718
result['value']['cash_discount'] = res['cash_discount']
720
result['value']['cash_discount'] = True
723
account_invoice_line()
726
class account_voucher_line_discount_to_use(osv.osv):
728
Dynamically generated discount lines that are applicable per voucher line
730
_name = "account.voucher.line.discount_to_use"
731
_rec_name = 'discount_window_date'
733
'voucher_line_id': fields.many2one('account.voucher.line', 'Account Voucher Line', ondelete='cascade', readonly=True),
734
'use_discount': fields.boolean('Use Discount', help='Used to indicate if the cash discount should be used/taken when calculating payment.',
736
'inv_payment_terms': fields.many2one('account.payment.term', 'Invoice Payment Terms', help='Payments terms description'),
737
'discount_window_date': fields.date('Discount Window Date',
738
help='The last day of the discount window.\
739
To receive discounts payments must be paid on or before this date.'),
740
'proposed_discount': fields.float('Proposed Discount',
741
help='This is the proposed full discount based on the Invoice Payment Terms and \
742
the Original Amount.', readonly=True),
743
'discount_amount': fields.float('Discount Amt', help='Enter the amount of discount to be given.', required=True),
744
'gl_account': fields.many2one('account.account', 'G/L Account',
745
help='Enter the General Ledger account number to record taking \
746
the cash discount.', required=True),
747
'credit_selected': fields.boolean('Credit Selected'),
751
'credit_selected': False,
754
def onchage_use_discount(self, cr, uid, ids, use_discount, proposed_discount, context=None):
756
Fill the value with proposed discount when use discount check box is clicked and remove the value when use discount is unchecked
760
res['value'] = {'discount_amount': proposed_discount}
762
res['value'] = {'discount_amount': 0}
763
res['value']['credit_selected'] = True
766
def onchage_discount_amount(self, cr, uid, ids, proposed_discount, discount_amount, context=None):
768
Function to check discount amount entered in discount line of payment line
771
if discount_amount < 0:
772
res['value'] = {'discount_amount': 0, 'use_discount': False }
773
res['warning'] = {'title': 'Discount not in the limit', 'message': 'Discount should not be a negative value.'}
775
elif discount_amount > proposed_discount:
776
res['value'] = {'discount_amount': proposed_discount, 'use_discount': True }
778
'title': 'Discount not in the limit',
779
'message': 'Please adjust the Discount Amt value to be less than or equal to the Proposed Discount.'
781
elif discount_amount == 0.0:
782
res['value'] = {'use_discount': False}
784
res['value'] = {'use_discount': True}
787
account_voucher_line_discount_to_use()
789
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: