494 lines
22 KiB
Python
494 lines
22 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import base64
|
|
import io
|
|
from collections import defaultdict
|
|
from datetime import date
|
|
|
|
from dateutil.relativedelta import relativedelta
|
|
from lxml import etree
|
|
|
|
from odoo import api, fields, models, _
|
|
from odoo.exceptions import UserError, ValidationError
|
|
from odoo.fields import Domain
|
|
from odoo.tools import format_date
|
|
from odoo.tools.misc import file_path
|
|
|
|
|
|
class L10n_Be274_Xx(models.Model):
|
|
_name = 'l10n_be.274_xx'
|
|
_description = '274.XX Sheets'
|
|
_order = 'date_start'
|
|
|
|
@api.model
|
|
def default_get(self, fields):
|
|
if self.env.company.country_id.code != "BE":
|
|
raise UserError(_('This feature seems to be as exclusive as Belgian chocolates. You must be logged in to a Belgian company to use it.'))
|
|
return super().default_get(fields)
|
|
|
|
year = fields.Integer(required=True, default=lambda self: fields.Date.today().year)
|
|
month = fields.Selection([
|
|
('1', 'January'),
|
|
('2', 'February'),
|
|
('3', 'March'),
|
|
('4', 'April'),
|
|
('5', 'May'),
|
|
('6', 'June'),
|
|
('7', 'July'),
|
|
('8', 'August'),
|
|
('9', 'September'),
|
|
('10', 'October'),
|
|
('11', 'November'),
|
|
('12', 'December'),
|
|
], required=True, default=lambda self: str((fields.Date.today() + relativedelta(months=-1)).month))
|
|
date_start = fields.Date(
|
|
'Start Period', store=True, readonly=False,
|
|
compute='_compute_dates')
|
|
date_end = fields.Date(
|
|
'End Period', store=True, readonly=False,
|
|
compute='_compute_dates')
|
|
line_ids = fields.One2many(
|
|
'l10n_be.274_xx.line', 'sheet_id',
|
|
compute='_compute_line_ids', store=True, readonly=False, compute_sudo=True)
|
|
company_id = fields.Many2one('res.company', default=lambda self: self.env.company)
|
|
currency_id = fields.Many2one('res.currency', related='company_id.currency_id')
|
|
state = fields.Selection([
|
|
('draft', 'Draft'),
|
|
('waiting', 'Waiting'),
|
|
('done', 'Done')], default='draft')
|
|
sheet_274_10 = fields.Binary('274.10 Sheet', readonly=True, attachment=False)
|
|
sheet_274_10_filename = fields.Char()
|
|
pp_amount = fields.Monetary("Withholding Taxes", compute='_compute_amounts', compute_sudo=True)
|
|
pp_amount_32 = fields.Monetary(compute='_compute_amounts', compute_sudo=True)
|
|
pp_amount_33 = fields.Monetary(compute='_compute_amounts', compute_sudo=True)
|
|
pp_amount_34 = fields.Monetary(compute='_compute_amounts', compute_sudo=True)
|
|
taxable_amount = fields.Monetary("Taxable Amount", compute='_compute_amounts', compute_sudo=True)
|
|
taxable_amount_32 = fields.Monetary(compute='_compute_amounts', compute_sudo=True)
|
|
taxable_amount_33 = fields.Monetary(compute='_compute_amounts', compute_sudo=True)
|
|
taxable_amount_34 = fields.Monetary(compute='_compute_amounts', compute_sudo=True)
|
|
deducted_amount = fields.Monetary("Exempted Amount", compute='_compute_amounts', compute_sudo=True)
|
|
deducted_amount_32 = fields.Monetary(compute='_compute_amounts', compute_sudo=True)
|
|
deducted_amount_33 = fields.Monetary(compute='_compute_amounts', compute_sudo=True)
|
|
deducted_amount_34 = fields.Monetary(compute='_compute_amounts', compute_sudo=True)
|
|
capped_amount_34 = fields.Monetary("Capped Amount", compute='_compute_amounts', compute_sudo=True)
|
|
xml_file = fields.Binary(string="XML file")
|
|
xml_filename = fields.Char()
|
|
xml_validation_state = fields.Selection([
|
|
('normal', "N/A"),
|
|
('done', "Valid"),
|
|
('invalid', "Invalid"),
|
|
], default='normal', compute='_compute_validation_state', store=True)
|
|
error_message = fields.Char(store=True, compute='_compute_validation_state')
|
|
payment_reference = fields.Char('Withholding Tax Payment Reference')
|
|
xls_file = fields.Binary(string="XLS file")
|
|
xls_filename = fields.Char()
|
|
|
|
@api.depends('date_start')
|
|
def _compute_display_name(self):
|
|
for record in self:
|
|
record.display_name = format_date(self.env, record.date_start, date_format="MMMM y", lang_code=self.env.user.lang)
|
|
|
|
@api.depends('year', 'month')
|
|
def _compute_dates(self):
|
|
for record in self:
|
|
record.update({
|
|
'date_start': date(record.year, int(record.month), 1),
|
|
'date_end': date(record.year, int(record.month), 1) + relativedelta(day=31),
|
|
})
|
|
|
|
@api.depends('xml_file')
|
|
def _compute_validation_state(self):
|
|
xsd_schema_file_path = file_path('l10n_be_hr_payroll/data/finprof.xsd')
|
|
xsd_root = etree.parse(xsd_schema_file_path)
|
|
schema = etree.XMLSchema(xsd_root)
|
|
for sheet in self:
|
|
if not sheet.xml_file:
|
|
sheet.xml_validation_state = 'normal'
|
|
sheet.error_message = False
|
|
else:
|
|
xml_root = etree.fromstring(base64.b64decode(sheet.xml_file))
|
|
try:
|
|
schema.assertValid(xml_root)
|
|
sheet.xml_validation_state = 'done'
|
|
except etree.DocumentInvalid as err:
|
|
sheet.xml_validation_state = 'invalid'
|
|
sheet.error_message = str(err)
|
|
|
|
def _get_valid_payslips(self):
|
|
domain = Domain([
|
|
('state', 'in', ['paid', 'validated']),
|
|
('company_id', '=', self.company_id.id),
|
|
('date_from', '>=', self.date_start),
|
|
('date_to', '<=', self.date_end),
|
|
])
|
|
if self.env.context.get('wizard_274xx_force_employee_ids'):
|
|
domain &= Domain('employee_id', 'in', self.env.context['wizard_274xx_force_employee_ids'])
|
|
return self.env['hr.payslip'].search(domain)
|
|
|
|
@api.constrains('company_id')
|
|
def _check_l10n_be_company_number(self):
|
|
for company in self.mapped('company_id'):
|
|
if not company.l10n_be_company_number or not company.l10n_be_revenue_code:
|
|
raise ValidationError(_("Please configure the 'Company Number' and the 'Revenue Code' on the Payroll Settings."))
|
|
|
|
@api.depends('date_start', 'date_end', 'company_id')
|
|
def _compute_line_ids(self):
|
|
monthly_pay = self.env.ref('l10n_be_hr_payroll.hr_payroll_structure_cp200_employee_salary')
|
|
|
|
for sheet in self:
|
|
mapped_pp = defaultdict(int)
|
|
mapped_taxable_amount = defaultdict(int)
|
|
payslips = sheet._get_valid_payslips()
|
|
|
|
line_values = payslips._get_line_values([
|
|
'GROSS', 'PPTOTAL',
|
|
'DOUBLE.DECEMBER.GROSS', 'DOUBLE.DECEMBER.P.P'], compute_sum=True)
|
|
|
|
payslips = payslips.filtered(lambda p: line_values['PPTOTAL'][p.id]['total'])
|
|
|
|
# Valid payslips for exemption
|
|
payslips = payslips.filtered(lambda p: p.version_id.rd_percentage and p.struct_id == monthly_pay)
|
|
|
|
for payslip in payslips:
|
|
if payslip.version_id.rd_percentage:
|
|
mapped_pp[payslip.employee_id] += payslip.version_id.rd_percentage / 100 * 0.8 * line_values['PPTOTAL'][payslip.id]['total']
|
|
mapped_taxable_amount[payslip.employee_id] += line_values['GROSS'][payslip.id]['total']
|
|
|
|
sheet.line_ids = [(5, 0, 0)] + [(0, 0, {
|
|
'sheet_id': sheet.id,
|
|
'employee_id': employee.id,
|
|
'amount': mapped_pp[employee],
|
|
'taxable_amount': mapped_taxable_amount[employee],
|
|
}) for employee in mapped_pp.keys()]
|
|
|
|
@api.depends('date_start', 'date_end', 'company_id')
|
|
def _compute_amounts(self):
|
|
monthly_pay = self.env.ref('l10n_be_hr_payroll.hr_payroll_structure_cp200_employee_salary')
|
|
|
|
for sheet in self:
|
|
payslips = sheet._get_valid_payslips()
|
|
|
|
line_values = payslips._get_line_values([
|
|
'GROSS', 'PPTOTAL',
|
|
'DOUBLE.DECEMBER.GROSS', 'DOUBLE.DECEMBER.P.P'], compute_sum=True)
|
|
|
|
payslips = payslips.filtered(lambda p: line_values['PPTOTAL'][p.id]['total'])
|
|
|
|
# Total
|
|
sheet.taxable_amount = line_values['GROSS']['sum']['total'] + line_values['DOUBLE.DECEMBER.GROSS']['sum']['total']
|
|
sheet.pp_amount = line_values['PPTOTAL']['sum']['total'] - line_values['DOUBLE.DECEMBER.P.P']['sum']['total']
|
|
# Valid payslips for exemption
|
|
payslips = payslips.filtered(lambda p: p.version_id.rd_percentage and p.struct_id == monthly_pay)
|
|
# 32 : Civil Engineers / Doctors
|
|
payslips_32 = payslips.filtered(lambda p: p.employee_id.certificate in ['doctor', 'civil_engineer'])
|
|
sheet.taxable_amount_32 = sum(line_values['GROSS'][p.id]['total'] for p in payslips_32)
|
|
sheet.pp_amount_32 = sum(line_values['PPTOTAL'][p.id]['total'] for p in payslips_32)
|
|
|
|
# 33 : Masters
|
|
payslips_33 = payslips.filtered(lambda p: p.employee_id.certificate in ['master'])
|
|
sheet.taxable_amount_33 = sum(line_values['GROSS'][p.id]['total'] for p in payslips_33)
|
|
sheet.pp_amount_33 = sum(line_values['PPTOTAL'][p.id]['total'] for p in payslips_33)
|
|
# 33 : Bachelors
|
|
payslips_34 = payslips.filtered(lambda p: p.employee_id.certificate in ['bachelor'])
|
|
sheet.taxable_amount_34 = sum(line_values['GROSS'][p.id]['total'] for p in payslips_34)
|
|
sheet.pp_amount_34 = sum(line_values['PPTOTAL'][p.id]['total'] for p in payslips_34)
|
|
sheet.deducted_amount = 0
|
|
sheet.deducted_amount_32 = 0
|
|
sheet.deducted_amount_33 = 0
|
|
sheet.deducted_amount_34 = 0
|
|
|
|
for payslip in payslips:
|
|
if payslip.version_id.rd_percentage:
|
|
deducted_amount = payslip.version_id.rd_percentage / 100 * 0.8 * line_values['PPTOTAL'][payslip.id]['total']
|
|
if payslip.employee_id.certificate in ['doctor', 'civil_engineer']:
|
|
sheet.deducted_amount_32 += deducted_amount
|
|
elif payslip.employee_id.certificate == 'master':
|
|
sheet.deducted_amount_33 += deducted_amount
|
|
elif payslip.employee_id.certificate == 'bachelor':
|
|
sheet.deducted_amount_34 += deducted_amount
|
|
sheet.deducted_amount += deducted_amount
|
|
|
|
# The total amount of the exemption from payment of the withholding tax granted to
|
|
# researchers who have a bachelor's degree is limited to 25% of the total amount of
|
|
# the exemption from the payment of the withholding tax granted to researchers who have
|
|
# a diploma qualifying as a doctorate or Master. This percentage will be doubled for
|
|
# small companies (article 15 §§, 1 to 6 of the Companies Code). This limitation has
|
|
# not changed on January 1, 2020.
|
|
# https://www.belspo.be/belspo/organisation/fisc_dipl_fr.stm
|
|
sheet.capped_amount_34 = min(
|
|
sheet.deducted_amount_34,
|
|
(sheet.deducted_amount_32 + sheet.deducted_amount_33) / 4)
|
|
|
|
def action_generate_pdf(self):
|
|
self.ensure_one()
|
|
report_data = {
|
|
'company_name': self.company_id.name,
|
|
'company_address': ' '.join([self.company_id.street or '', self.company_id.street2 or '']),
|
|
'company_zip': self.company_id.zip,
|
|
'company_city': self.company_id.city,
|
|
'company_phone': self.company_id.phone,
|
|
'date_start': self.date_start.strftime("%d/%m/%Y"),
|
|
'date_end': self.date_end.strftime("%d/%m/%Y"),
|
|
}
|
|
|
|
filename = '%s-%s-274_XX.pdf' % (self.date_start.strftime("%d%B%Y"), self.date_end.strftime("%d%B%Y"))
|
|
export_274_sheet_pdf, _ = self.env["ir.actions.report"].sudo()._render_qweb_pdf(
|
|
self.env.ref('l10n_be_hr_payroll.action_report_employee_274_10'),
|
|
res_ids=self.ids, data=report_data)
|
|
|
|
self.sheet_274_10_filename = filename
|
|
self.sheet_274_10 = base64.encodebytes(export_274_sheet_pdf)
|
|
self.state = 'waiting'
|
|
|
|
def _to_eurocent(self, amount):
|
|
return '%s' % int(amount * 100)
|
|
|
|
def _get_rendering_data(self):
|
|
def _to_eurocent(amount):
|
|
return '%s' % int(amount * 100)
|
|
|
|
# https://finances.belgium.be/sites/default/files/downloads/Sp%C3%A9cification%20XML.doc
|
|
result = {
|
|
'creation_date': fields.Date.today().strftime("%Y-%m-%d"),
|
|
'last_period': self.date_start.strftime("%y%m"),
|
|
'declarations': [],
|
|
'positive_amount': 1,
|
|
'positive_total': 0,
|
|
'negative_amount': 3,
|
|
'negative_total': 0,
|
|
}
|
|
payslips = self._get_valid_payslips()
|
|
|
|
if len(payslips.mapped('company_id')) > 1:
|
|
raise UserError(_('The payslips should be from the same company.'))
|
|
if not payslips:
|
|
raise UserError(_('There is no valid payslip to declare.'))
|
|
|
|
year_period_code = {
|
|
2: '6',
|
|
3: '7',
|
|
0: '8',
|
|
1: '9',
|
|
}
|
|
reference_number = payslips[0].company_id.l10n_be_company_number
|
|
# payment reference - 12 Characters
|
|
first_10_characters = "%s%s%s" % (
|
|
reference_number[1:8], # 1 - 7
|
|
year_period_code[payslips[0].date_from.year % 4], # 8
|
|
str(payslips[0].date_from.month).zfill(2), # 9-10
|
|
)
|
|
payment_reference = "%s%s" % (
|
|
first_10_characters,
|
|
str(int(first_10_characters) % 97 or 97).zfill(2),
|
|
)
|
|
self.payment_reference = "+++ %s / %s / %s +++" % (
|
|
payment_reference[0:3],
|
|
payment_reference[3:7],
|
|
payment_reference[7:12],
|
|
)
|
|
date_from = payslips[0].date_from
|
|
date_to = payslips[0].date_to
|
|
district = payslips[0].company_id.l10n_be_revenue_code[:2]
|
|
office = payslips[0].company_id.l10n_be_revenue_code[-2:]
|
|
|
|
invalid_payslips = payslips.filtered(lambda p: p.date_from != date_from or p.date_to != date_to)
|
|
if invalid_payslips:
|
|
raise UserError(_('The payslips should cover the same period:\n%s', '\n'.join(invalid_payslips.mapped('name'))))
|
|
|
|
declaration_10 = {
|
|
'declaration_number': 10000010,
|
|
'reference_number': reference_number,
|
|
'year': date_from.strftime("%Y"),
|
|
'period': '%s00' % (date_from.strftime("%m")),
|
|
'revenue_nature': 10,
|
|
'taxable_revenue': 0,
|
|
'prepayment': 0,
|
|
'payment_reference': payment_reference,
|
|
'district': district,
|
|
'office': office,
|
|
}
|
|
declaration_32 = {
|
|
'declaration_number': 10000032,
|
|
'reference_number': reference_number,
|
|
'year': date_from.strftime("%Y"),
|
|
'period': '%s00' % (date_from.strftime("%m")),
|
|
'revenue_nature': 32,
|
|
'taxable_revenue': 0,
|
|
'prepayment': 0,
|
|
'payment_reference': payment_reference,
|
|
'district': district,
|
|
'office': office,
|
|
}
|
|
declaration_33 = {
|
|
'declaration_number': 10000033,
|
|
'reference_number': reference_number,
|
|
'year': date_from.strftime("%Y"),
|
|
'period': '%s00' % (date_from.strftime("%m")),
|
|
'revenue_nature': 33,
|
|
'taxable_revenue': 0,
|
|
'prepayment': 0,
|
|
'payment_reference': payment_reference,
|
|
'district': district,
|
|
'office': office,
|
|
}
|
|
declaration_34 = {
|
|
'declaration_number': 10000034,
|
|
'reference_number': reference_number,
|
|
'year': date_from.strftime("%Y"),
|
|
'period': '%s00' % (date_from.strftime("%m")),
|
|
'revenue_nature': 34,
|
|
'taxable_revenue': 0,
|
|
'prepayment': 0,
|
|
'payment_reference': payment_reference,
|
|
'district': district,
|
|
'office': office,
|
|
}
|
|
monthly_pay = self.env.ref('l10n_be_hr_payroll.hr_payroll_structure_cp200_employee_salary')
|
|
|
|
line_values = payslips._get_line_values([
|
|
'GROSS', 'PPTOTAL',
|
|
'DOUBLE.DECEMBER.GROSS', 'DOUBLE.DECEMBER.P.P'])
|
|
|
|
for payslip in payslips:
|
|
pp_total = line_values['PPTOTAL'][payslip.id]['total'] \
|
|
- line_values['DOUBLE.DECEMBER.P.P'][payslip.id]['total']
|
|
pp_total_eurocent = pp_total
|
|
taxable_eurocent = line_values['GROSS'][payslip.id]['total'] + line_values['DOUBLE.DECEMBER.GROSS'][payslip.id]['total']
|
|
declaration_10['prepayment'] += pp_total_eurocent
|
|
declaration_10['taxable_revenue'] += taxable_eurocent
|
|
result['positive_total'] += pp_total_eurocent
|
|
|
|
if payslip.struct_id == monthly_pay and payslip.version_id.rd_percentage:
|
|
employee = payslip.employee_id
|
|
deduction = - payslip.version_id.rd_percentage / 100 * 0.8 * line_values['PPTOTAL'][payslip.id]['total']
|
|
if deduction:
|
|
pp_total_eurocent = deduction
|
|
taxable_eurocent = line_values['GROSS'][payslip.id]['total']
|
|
if employee.certificate in ['doctor', 'civil_engineer']:
|
|
declaration_32['prepayment'] += pp_total_eurocent
|
|
declaration_32['taxable_revenue'] += taxable_eurocent
|
|
elif employee.certificate == 'master':
|
|
declaration_33['prepayment'] += pp_total_eurocent
|
|
declaration_33['taxable_revenue'] += taxable_eurocent
|
|
elif employee.certificate == 'bachelor':
|
|
declaration_34['prepayment'] += pp_total_eurocent
|
|
declaration_34['taxable_revenue'] += taxable_eurocent
|
|
result['negative_total'] += deduction
|
|
|
|
# The total amount of the exemption from payment of the withholding tax granted to
|
|
# researchers who have a bachelor's degree is limited to 25% of the total amount of
|
|
# the exemption from the payment of the withholding tax granted to researchers who have
|
|
# a diploma qualifying as a doctorate or Master. This percentage will be doubled for
|
|
# small companies (article 15 §§, 1 to 6 of the Companies Code). This limitation has
|
|
# not changed on January 1, 2020.
|
|
# https://www.belspo.be/belspo/organisation/fisc_dipl_fr.stm
|
|
declaration_34['prepayment'] = -min(
|
|
- declaration_34['prepayment'],
|
|
(- declaration_32['prepayment'] - declaration_33['prepayment']) / 4.0)
|
|
|
|
result['positive_total'] = _to_eurocent(result['positive_total'])
|
|
result['negative_total'] = str(
|
|
int(_to_eurocent(declaration_32['prepayment'])) + \
|
|
int(_to_eurocent(declaration_33['prepayment'])) + \
|
|
int(_to_eurocent(declaration_34['prepayment'])))
|
|
|
|
for declaration in [declaration_10, declaration_32, declaration_33, declaration_34]:
|
|
declaration['prepayment'] = _to_eurocent(declaration['prepayment'])
|
|
declaration['taxable_revenue'] = _to_eurocent(declaration['taxable_revenue'])
|
|
|
|
result['declarations'] = [declaration_10, declaration_32, declaration_33, declaration_34]
|
|
return result
|
|
|
|
def action_generate_xml(self):
|
|
filename = '%s-%s-finprof.xml' % (self.date_start.strftime("%d%B%Y"), self.date_end.strftime("%d%B%Y"))
|
|
self.xml_filename = filename
|
|
|
|
xml_str = self.env['ir.qweb']._render('l10n_be_hr_payroll.finprof_xml_report',
|
|
self._get_rendering_data())
|
|
|
|
# Prettify xml string
|
|
root = etree.fromstring(xml_str, parser=etree.XMLParser(remove_blank_text=True))
|
|
xml_formatted_str = etree.tostring(root, pretty_print=True, encoding='utf-8', xml_declaration=True)
|
|
|
|
self.xml_file = base64.encodebytes(xml_formatted_str)
|
|
|
|
self.state = 'waiting'
|
|
|
|
def action_generate_xls(self):
|
|
output = io.BytesIO()
|
|
import xlsxwriter # noqa: PLC0415
|
|
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
|
|
worksheet = workbook.add_worksheet('Exemption Details')
|
|
style_highlight = workbook.add_format({'bold': True, 'pattern': 1, 'bg_color': '#E0E0E0', 'align': 'center'})
|
|
style_normal = workbook.add_format({'align': 'center'})
|
|
row = 0
|
|
|
|
headers = [
|
|
"Nom Complet",
|
|
"Numéro National",
|
|
"Département",
|
|
"Date d'entrée",
|
|
"Date de sortie",
|
|
"Certification",
|
|
"Fonction",
|
|
r"Recherche Scientifique \%",
|
|
"Montant Imposable",
|
|
"Dispense de Vers. Recherche Scientifique",
|
|
]
|
|
|
|
certificate_selection_vals = {
|
|
elem[0]: elem[1] for elem in self.env['hr.employee']._fields['certificate']._description_selection(self.env)
|
|
}
|
|
|
|
rows = []
|
|
for line in self.line_ids:
|
|
employee = line.employee_id.sudo()
|
|
|
|
rows.append((
|
|
employee.legal_name,
|
|
employee.identification_id,
|
|
employee.department_id.name or employee.version_id.department_id.name,
|
|
employee.contract_date_start.strftime("%d-%m-%Y") if employee.contract_date_start else '',
|
|
employee.departure_date.strftime("%d-%m-%Y") if employee.departure_date else '',
|
|
certificate_selection_vals[employee.certificate],
|
|
employee.job_title or employee.job_id.name,
|
|
employee.version_id.rd_percentage,
|
|
line.taxable_amount,
|
|
line.amount,
|
|
))
|
|
|
|
col = 0
|
|
for header in headers:
|
|
worksheet.write(row, col, header, style_highlight)
|
|
worksheet.set_column(col, col, 30)
|
|
col += 1
|
|
|
|
row = 1
|
|
for employee_row in rows:
|
|
col = 0
|
|
for employee_data in employee_row:
|
|
worksheet.write(row, col, employee_data, style_normal)
|
|
col += 1
|
|
row += 1
|
|
|
|
workbook.close()
|
|
xlsx_data = output.getvalue()
|
|
|
|
self.xls_file = base64.encodebytes(xlsx_data)
|
|
self.xls_filename = "withholding_tax_exemption_details.xlsx"
|
|
|
|
self.state = 'waiting'
|
|
|
|
|
|
class L10n_Be274_XxLine(models.Model):
|
|
_name = 'l10n_be.274_xx.line'
|
|
_description = '274.XX Sheets Line'
|
|
|
|
sheet_id = fields.Many2one('l10n_be.274_xx', index=True)
|
|
employee_id = fields.Many2one('hr.employee')
|
|
certificate = fields.Selection(related='employee_id.certificate')
|
|
taxable_amount = fields.Monetary()
|
|
amount = fields.Monetary(string="Exempted Amount")
|
|
company_id = fields.Many2one('res.company', related='sheet_id.company_id')
|
|
currency_id = fields.Many2one('res.currency', related='company_id.currency_id')
|