Files
tryton/payment_term.py
T

224 lines
8.4 KiB
Python

#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from trytond.osv import fields, OSV
from trytond.sql_db import table_handler
from decimal import Decimal
import datetime
import mx.DateTime
class PaymentTerm(OSV):
'Payment Term'
_name = 'account.invoice.payment_term'
_description = __doc__
name = fields.Char('Payment Term', size=None, required=True, translate=True)
active = fields.Boolean('Active')
description = fields.Text('Description', translate=True)
lines = fields.One2Many('account.invoice.payment_term.line', 'payment',
'Lines')
def __init__(self):
super(PaymentTerm, self).__init__()
self._order.insert(0, ('name', 'ASC'))
self._error_messages.update({
'invalid_line': 'Invalid payment term line!',
'missing_remainder': 'Payment term missing a remainder line!',
})
def default_active(self, cursor, user, context=None):
return True
def compute(self, cursor, user, amount, currency, payment_term, date=None,
context=None):
'''
Return list with (date, amount) for each payment term lines
'''
#TODO implement business_days
# http://pypi.python.org/pypi/BusinessHours/
type_obj = self.pool.get('account.invoice.payment_term.line.type')
delay_obj = self.pool.get(
'account.invoice.payment_term.line.delay')
currency_obj = self.pool.get('currency.currency')
date_obj = self.pool.get('ir.date')
res = []
if date is None:
date = date_obj.today(cursor, user, context=context)
remainder = amount
for line in payment_term.lines:
value = type_obj.get_value(cursor, user, line, remainder, currency,
context)
value_date = delay_obj.get_date(cursor, user, line, date, context)
if not value or not value_date:
if (not remainder) and line.amount:
self.raise_user_error(cursor, 'invalid_line',
context=context)
else:
continue
res.append((value_date, value))
remainder -= value
if not currency_obj.is_zero(cursor, user, currency, remainder):
self.raise_user_error(cursor, 'missing_remainder',
context=context)
return res
PaymentTerm()
class PaymentTermLineType(OSV):
'Payment Term Line Type'
_name = 'account.invoice.payment_term.line.type'
_description = __doc__
name = fields.Char('Name', size=None, translate=True, required=True)
code = fields.Char('Code', size=None, required=True)
def __init__(self):
super(PaymentTermLineType, self).__init__()
self._sql_constraints += [
('code_uniq', 'UNIQUE(code)', 'Code must be unique!'),
]
self._order.insert(0, ('name', 'ASC'))
def get_value(self, cursor, user, line, amount, currency, context=None):
currency_obj = self.pool.get('currency.currency')
if line.type == 'fixed':
return currency_obj.compute(cursor, user, line.currency,
line.amount, currency, context=context)
elif line.type == 'percent':
return currency_obj.round(cursor, user, currency,
amount * line.percentage / Decimal('100'))
elif line.type == 'remainder':
return currency_obj.round(cursor, user, currency, amount)
return None
PaymentTermLineType()
class PaymentTermLineDelay(OSV):
'Payment Term Line Delay'
_name = 'account.invoice.payment_term.line.delay'
_description = __doc__
name = fields.Char('Name', size=None, translate=True, required=True)
code = fields.Char('Code', size=None, required=True)
def __init__(self):
super(PaymentTermLineDelay, self).__init__()
self._sql_constraints += [
('code_uniq', 'UNIQUE(code)', 'Code must be unique!'),
]
self._order.insert(0, ('name', 'ASC'))
def get_date(self, cursor, user, line, date, context=None):
value = None
if line.delay == 'net_days':
value = mx.DateTime.strptime(str(date), '%Y-%m-%d') + \
mx.DateTime.RelativeDateTime(days=line.days)
elif line.delay == 'end_month':
value = mx.DateTime.strptime(str(date), '%Y-%m-%d') + \
mx.DateTime.RelativeDateTime(days=line.days) + \
mx.DateTime.RelativeDateTime(day=-1)
if value:
return datetime.date(value.year, value.month, value.day)
return None
PaymentTermLineDelay()
class PaymentTermLine(OSV):
'Payment Term Line'
_name = 'account.invoice.payment_term.line'
_description = __doc__
sequence = fields.Integer('Sequence',
help='Use to order lines in ascending order')
payment = fields.Many2One('account.invoice.payment_term', 'Payment Term',
required=True, ondelete="CASCADE")
type = fields.Selection('get_type', 'Type', required=True,
on_change=['type'])
percentage = fields.Numeric('Percentage', digits=(16, 8),
states={
'invisible': "type != 'percent'",
'required': "type == 'percent'",
}, help='In %')
amount = fields.Numeric('Amount', digits="(16, currency_digits)",
states={
'invisible': "type != 'fixed'",
'required': "type == 'fixed'",
})
currency = fields.Many2One('currency.currency', 'Currency',
states={
'invisible': "type != 'fixed'",
'required': "type == 'fixed'",
})
currency_digits = fields.Function('get_currency_digits', type='integer',
string='Currency Digits', on_change_with=['currency'])
days = fields.Integer('Number of Days')
delay = fields.Selection('get_delay', 'Condition', required=True)
def __init__(self):
super(PaymentTermLine, self).__init__()
self._order.insert(0, ('sequence', 'ASC'))
def _auto_init(self, cursor, module_name):
super(PaymentTermLine, self)._auto_init(cursor, module_name)
table = table_handler(cursor, self._table, self._name, module_name)
# Migration from 1.0 percent change into percentage
if 'percent' in table.table:
cursor.execute('UPDATE "' + self._table + '" ' \
'SET percentage = percent * 100')
cursor.execute('ALTER TABLE "' + self._table + '" ' \
'DROP COLUMN percent')
def default_type(self, cursor, user, context=None):
return 'remainder'
def default_delay(self, cursor, user, context=None):
return 'net_days'
def get_type(self, cursor, user, context=None):
type_obj = self.pool.get('account.invoice.payment_term.line.type')
type_ids = type_obj.search(cursor, user, [], context=context)
types = type_obj.browse(cursor, user, type_ids, context=context)
return [(x.code, x.name) for x in types]
def get_delay(self, cursor, user, context=None):
delay_obj = self.pool.get('account.invoice.payment_term.line.delay')
delay_ids = delay_obj.search(cursor, user, [], context=context)
delays = delay_obj.browse(cursor, user, delay_ids,
context=context)
return [(x.code, x.name) for x in delays]
def on_change_type(self, cursor, user, ids, vals, context=None):
if not 'type' in vals:
return {}
res = {}
if vals['type'] != 'fixed':
res['amount'] = Decimal('0.0')
res['currency'] = False
if vals['type'] != 'percent':
res['percentage'] = Decimal('0.0')
return res
def on_change_with_currency_digits(self, cursor, user, ids, vals,
context=None):
currency_obj = self.pool.get('currency.currency')
if vals.get('currency'):
currency = currency_obj.browse(cursor, user, vals['currency'],
context=context)
return currency.digits
return 2
def get_currency_digits(self, cursor, user, ids, name, arg, context=None):
res = {}
for line in self.browse(cursor, user, ids, context=context):
if line.currency:
res[line.id] = line.currency.digits
else:
res[line.id] = 2
return res
PaymentTermLine()