As a remote company, Hibou has dealt with many a state and federal remittance for payroll. How many, you may ask? Well, we currently have employees in nine different states and each state has different reporting requirements.
Some states - like Florida - are as easy going as can be. An RT-6 (Re-Employment Tax) once per quarter and you're set! Illinois, on the other hand, requires no less than 21 interactions with withholding agencies - more if you're on a semi-weekly payment schedule - per year:
Monthly tax withholding payments
Quarterly reports for tax withholding
Quarterly reports for unemployment
Annual submission of the W-2 to the state
All of this to say that we know our way around payroll reporting. I used payslip line reporting for years to determine the values needed to process each report or remittance and that's worked wonderfully. Just a few clicks and knowing what records would have the data I needed allowed me to easily capture the information and each report typically only takes a few minutes to file. However, we recently started to ponder: "If we spent a bit of time using the tools available to any Odoo user, could we simplify the process?"
The answer, naturally, was a resounding "YES!"
Let's take the W2, for example. If you work in payroll, you likely know that you can enter W2s on ssa.gov. The below server action makes it super simple to quickly copy and paste your values per W2, at which point you can submit he W2s to the IRS and print them for mailing to employees. (Note that the codes to sum this data comes from Hibou's USA Payroll localization and that this server action can - and should - be modified to meet your company's unique codes and/or withholding options.)
Name: Generate W2
Model: Pay Slip
Action to Do: Execute Python Code
Python Code:
employee_w2_data = {}
for payslip in records:
employee = payslip.employee_id
if employee not in employee_w2_data:
address = employee.address_home_id
employee_w2_data[employee] = {
'00.box_a': employee.identification_id or '',
'00.box_e': employee.name,
'00.box_f': address.contact_address,
'01.box_1': 0.0, # Wages Tips (taxable gross)
'02.box_2': 0.0, # FIT
'03.box_3': 0.0, # SS Wages
'04.box_4': 0.0, # SS
'05.box_5': 0.0, # Med Wages
'06.box_6': 0.0, # Med
'12.box_12_d': 0.0, # 401k Contribution
'12.box_12_aa': 0.0, # Roth 401k Contribution
'12.box_12_dd': 0.0, # Employee Medical Costs
}
w2_data = employee_w2_data[employee]
for line in payslip.line_ids:
if line.code == 'EE_US_941_FIT':
w2_data['01.box_1'] += line.amount
w2_data['02.box_2'] -= line.total
elif line.code == 'EE_US_941_FICA_SS':
w2_data['03.box_3'] += line.amount
w2_data['04.box_4'] -= line.total
elif line.code == 'EE_US_941_FICA_M':
w2_data['05.box_5'] += line.amount
w2_data['06.box_6'] -= line.total
elif line.code == 'EE_IRA':
w2_data['12.box_12_d'] -= line.amount
elif line.code == 'EE_IRA_ROTH':
w2_data['12.box_12_aa'] -= line.amount
elif line.code == 'EE_MED':
w2_data['12.box_12_dd'] -= line.amount
else:
# states possible...
code_pieces = line.code.split('_')
if len(code_pieces) >= 4 and (code_pieces[0], code_pieces[1], code_pieces[3]) == ('EE', 'US', 'SIT'):
state_code = code_pieces[2]
if ('15.box_15_' + state_code) not in w2_data:
w2_data[('15.box_15_' + state_code)] = state_code
w2_data[('16.box_16_' + state_code)] = 0.0
w2_data[('17.box_17_' + state_code)] = 0.0
w2_data[('18.box_18_' + state_code)] = 0.0
w2_data[('19.box_19_' + state_code)] = 0.0
w2_data[('20.box_20_' + state_code)] = ''
w2_data[('16.box_16_' + state_code)] += line.amount
w2_data[('17.box_17_' + state_code)] -= line.total
w2_data[('18.box_18_' + state_code)] += 0.0
w2_data[('19.box_19_' + state_code)] += 0.0
message = ''
for employee, w2_data in employee_w2_data.items():
employee_message = '\n\n%s ::\n' % (employee.name, )
for box_key in sorted(w2_data.keys()):
value = w2_data[box_key]
if isinstance(value, float):
employee_message += ' %s: %0.2f\n' % (box_key, value)
else:
employee_message += ' %s: %s\n' % (box_key, value)
message += employee_message
raise Warning(message)
report_data = [{'employee_id': e.id, 'employee_data': w2_data} for employee, w2_data in employee_w2_data.items()]
action = ''
After saving, don't forget to Create Contextual Action.
To use this server action, navigate to Employee Payslips then add a filter for Date Account to cover the reporting period. Select all payslips then use your Action to Generate W2. The result is a popup that displays all necessary information for filing (listing all employees and all values for selected payslips)!
Ready to Learn More About Server Actions?
Simplify everyday tasks with server actions and automated actions, which you can learn more about with the free preview videos in our Advanced Development in Odoo 13 course!