2020-04-16 00:37:53 +02:00
|
|
|
# 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 decimal import Decimal
|
|
|
|
from trytond.model import ModelView, ModelSQL, fields
|
|
|
|
from trytond.pyson import Not, Bool, Eval, If
|
|
|
|
from trytond.transaction import Transaction
|
2021-06-04 23:04:40 +02:00
|
|
|
from trytond.i18n import gettext
|
|
|
|
from .exceptions import WageTypeValidationError
|
2021-09-20 21:41:50 +02:00
|
|
|
from trytond.tools import lstrip_wildcard
|
2021-12-03 20:41:04 +01:00
|
|
|
from trytond.pool import Pool
|
2020-04-16 00:37:53 +02:00
|
|
|
|
2021-01-22 03:26:22 +01:00
|
|
|
STATES = {'readonly': Not(Bool(Eval('active')))}
|
2020-04-16 00:37:53 +02:00
|
|
|
|
|
|
|
|
|
|
|
class WageType(ModelSQL, ModelView):
|
|
|
|
"Wage Type"
|
|
|
|
__name__ = "staff.wage_type"
|
|
|
|
name = fields.Char('Name', select=True, required=True)
|
|
|
|
sequence = fields.Integer('Sequence', select=True)
|
|
|
|
code = fields.Char('Code', select=True)
|
|
|
|
active = fields.Boolean('Active')
|
|
|
|
company = fields.Many2One('company.company', 'Company', required=True,
|
|
|
|
domain=[
|
|
|
|
('id', If(Eval('context', {}).contains('company'), '=', '!='),
|
|
|
|
Eval('context', {}).get('company', 0)),
|
|
|
|
])
|
|
|
|
definition = fields.Selection([
|
|
|
|
('payment', 'Payment'),
|
|
|
|
('discount', 'Discount'),
|
|
|
|
('deduction', 'Deduction'),
|
|
|
|
], 'Definition', required=True)
|
|
|
|
unit_price_formula = fields.Char('Unit Price Formula',
|
2021-12-03 20:41:04 +01:00
|
|
|
help="""Python expression for eval(expr) \b
|
|
|
|
-salary: the salary employee \b
|
|
|
|
-minimum_salary: the salary minimum fixed """)
|
2020-04-16 00:37:53 +02:00
|
|
|
expense_formula = fields.Char('Expense Formula',
|
2021-12-03 20:41:04 +01:00
|
|
|
help="""Python expression for eval(expr) \b
|
|
|
|
-salary: the salary employee \b
|
|
|
|
-minimum_salary: the salary minimum fixed """)
|
2020-04-16 00:37:53 +02:00
|
|
|
uom = fields.Many2One('product.uom', 'UOM', required=True)
|
|
|
|
default_quantity = fields.Numeric('Default Quantity', digits=(16, 2))
|
|
|
|
type_concept = fields.Selection([
|
|
|
|
('salary', 'Salary'),
|
|
|
|
('extras', 'Extras'),
|
2022-05-03 16:34:42 +02:00
|
|
|
('unpaid_leave', 'Unpaid Leave'),
|
2020-04-16 00:37:53 +02:00
|
|
|
('other', 'Other'),
|
|
|
|
('special', 'Special'),
|
|
|
|
('', ''),
|
|
|
|
], 'Type Concept')
|
|
|
|
salary_constitute = fields.Boolean('Salary Constitute',
|
|
|
|
select=True,
|
|
|
|
states={
|
|
|
|
'invisible': Eval('definition') != 'payment',
|
|
|
|
})
|
|
|
|
debit_account = fields.Many2One('account.account',
|
|
|
|
'Debit Account', domain=[
|
2021-06-09 17:00:36 +02:00
|
|
|
('company', '=', Eval('context', {}).get('company', 0)),
|
|
|
|
('type', '!=', None),
|
|
|
|
],
|
2020-04-16 00:37:53 +02:00
|
|
|
states={
|
|
|
|
'invisible': ~Eval('context', {}).get('company'),
|
|
|
|
})
|
|
|
|
credit_account = fields.Many2One('account.account',
|
|
|
|
'Credit Account', domain=[
|
2021-06-09 17:00:36 +02:00
|
|
|
('type', '!=', None),
|
2020-04-16 00:37:53 +02:00
|
|
|
('company', '=', Eval('context', {}).get('company', 0))],
|
|
|
|
states={
|
|
|
|
'invisible': ~Eval('context', {}).get('company'),
|
|
|
|
})
|
|
|
|
deduction_account = fields.Many2One('account.account',
|
|
|
|
'Deduction Account', domain=[
|
2021-06-09 17:00:36 +02:00
|
|
|
('type', '!=', None),
|
2020-04-16 00:37:53 +02:00
|
|
|
('company', '=', Eval('context', {}).get('company', 0))],
|
|
|
|
states={
|
|
|
|
'invisible': Eval('definition') == 'payment',
|
|
|
|
'required': Eval('definition') != 'payment',
|
|
|
|
}, depends=['definition'])
|
|
|
|
receipt = fields.Boolean('Receipt', select=True)
|
|
|
|
parent = fields.Many2One('staff.wage_type', 'Parent', select=True)
|
|
|
|
concepts_salary = fields.Many2Many('staff.wage_type-staff.wage_type',
|
|
|
|
'parent', 'child', 'Concepts Salary')
|
|
|
|
contract_finish = fields.Boolean('Contract Finish', select=True)
|
|
|
|
party_required = fields.Boolean('Party Required', select=True)
|
|
|
|
limit_days = fields.Numeric('Limit Days', digits=(4, 0), states={
|
|
|
|
'invisible': Eval('type_concept') != 'special',
|
|
|
|
'required': Eval('type_concept') == 'special',
|
|
|
|
})
|
2022-10-26 23:19:27 +02:00
|
|
|
rate_employee = fields.Numeric('Rate Employee', digits=(16,5))
|
|
|
|
rate_company = fields.Numeric('Rate Company', digits=(16,5))
|
2020-04-16 00:37:53 +02:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def __setup__(cls):
|
|
|
|
super(WageType, cls).__setup__()
|
|
|
|
cls._order.insert(0, ('sequence', 'ASC'))
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def default_company():
|
|
|
|
return Transaction().context.get('company')
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def validate(cls, wage_types):
|
|
|
|
super(WageType, cls).validate(wage_types)
|
|
|
|
for wage in wage_types:
|
|
|
|
test_salary = {'salary': 1000}
|
|
|
|
if wage.unit_price_formula:
|
2023-05-01 12:25:06 +02:00
|
|
|
cls.compute_formula(wage.unit_price_formula, test_salary)
|
2020-04-16 00:37:53 +02:00
|
|
|
if wage.expense_formula:
|
2023-05-01 12:25:06 +02:00
|
|
|
cls.compute_formula(wage.expense_formula, test_salary)
|
2020-04-16 00:37:53 +02:00
|
|
|
|
2021-09-20 21:41:50 +02:00
|
|
|
@classmethod
|
|
|
|
def search_rec_name(cls, name, clause):
|
|
|
|
if clause[1].startswith('!') or clause[1].startswith('not '):
|
|
|
|
bool_op = 'AND'
|
|
|
|
else:
|
|
|
|
bool_op = 'OR'
|
|
|
|
code_value = clause[2]
|
|
|
|
if clause[1].endswith('like'):
|
|
|
|
code_value = lstrip_wildcard(clause[2])
|
|
|
|
return [bool_op,
|
|
|
|
('name',) + tuple(clause[1:]),
|
|
|
|
('code', clause[1], code_value) + tuple(clause[3:]),
|
|
|
|
]
|
|
|
|
|
2020-04-16 00:37:53 +02:00
|
|
|
@staticmethod
|
|
|
|
def default_unit_price_formula():
|
|
|
|
return 'salary'
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def default_active():
|
|
|
|
return True
|
|
|
|
|
2023-04-29 18:14:49 +02:00
|
|
|
@classmethod
|
|
|
|
def compute_unit_price(cls, formula, args):
|
|
|
|
return cls.compute_formula(formula, args)
|
2020-04-16 00:37:53 +02:00
|
|
|
|
2023-04-29 18:14:49 +02:00
|
|
|
@classmethod
|
|
|
|
def compute_expense(cls, formula, args):
|
|
|
|
return cls.compute_formula(formula, args)
|
2020-04-16 00:37:53 +02:00
|
|
|
|
2023-04-29 18:14:49 +02:00
|
|
|
def compute_formula2(self, formula, args=None):
|
2020-04-16 00:37:53 +02:00
|
|
|
'''
|
|
|
|
Compute a formula field with a salary value as float
|
|
|
|
:return: A decimal
|
|
|
|
'''
|
2021-12-03 20:41:04 +01:00
|
|
|
Configuration = Pool().get('staff.configuration')
|
|
|
|
configuration = Configuration(1)
|
|
|
|
minimum_salary = float(configuration.minimum_salary or 0)
|
2020-04-16 00:37:53 +02:00
|
|
|
formula = getattr(self, formula)
|
|
|
|
if not formula:
|
|
|
|
return Decimal('0.0')
|
|
|
|
if args.get('salary') != None:
|
|
|
|
salary = float(args['salary'])
|
|
|
|
try:
|
2021-08-19 23:59:52 +02:00
|
|
|
value = Decimal(str(round(eval(formula), 2)))
|
2020-04-16 00:37:53 +02:00
|
|
|
return value
|
|
|
|
except Exception:
|
2021-06-04 23:04:40 +02:00
|
|
|
raise WageTypeValidationError(
|
|
|
|
gettext('staff_payroll.msg_invalid_formula', formula=formula))
|
2020-04-16 00:37:53 +02:00
|
|
|
|
2023-04-29 18:14:49 +02:00
|
|
|
@classmethod
|
|
|
|
def compute_formula(cls, formula, args=None):
|
|
|
|
'''
|
|
|
|
Compute a formula field with a salary value as float
|
|
|
|
:return: A decimal
|
|
|
|
'''
|
|
|
|
# Configuration = Pool().get('staff.configuration')
|
|
|
|
# configuration = Configuration(1)
|
|
|
|
# minimum_salary = float(configuration.minimum_salary or 0)
|
|
|
|
# formula = getattr(self, formula)
|
|
|
|
if not formula:
|
|
|
|
return Decimal('0.0')
|
|
|
|
if args.get('salary') != None:
|
|
|
|
salary = float(args['salary'])
|
2023-05-17 19:24:44 +02:00
|
|
|
print('formula ....', formula)
|
|
|
|
if 1: #try:
|
2023-04-29 18:14:49 +02:00
|
|
|
value = Decimal(str(round(eval(formula), 2)))
|
|
|
|
return value
|
2023-05-17 19:24:44 +02:00
|
|
|
# except Exception:
|
|
|
|
# raise WageTypeValidationError(
|
|
|
|
# gettext('staff_payroll.msg_invalid_formula', formula=formula))
|
2023-04-29 18:14:49 +02:00
|
|
|
|
2020-04-16 00:37:53 +02:00
|
|
|
|
|
|
|
class WageTypeSalary(ModelSQL):
|
|
|
|
"Wage Type Salary"
|
|
|
|
__name__ = "staff.wage_type-staff.wage_type"
|
|
|
|
_table = 'staff_wage_type_salary_rel'
|
|
|
|
parent = fields.Many2One('staff.wage_type', 'Parent', ondelete='CASCADE',
|
|
|
|
select=True, required=True)
|
|
|
|
child = fields.Many2One('staff.wage_type', 'Child', ondelete='RESTRICT',
|
|
|
|
select=True, required=True)
|