meta_payroll_type: Refactor for more code reuse.

This commit is contained in:
Brett Smith 2020-12-23 17:27:30 -05:00
parent 9d41f79c52
commit 7c18bc221f
3 changed files with 79 additions and 44 deletions

View file

@ -67,7 +67,12 @@ class HookRegistry:
'.meta_repo_links': None, '.meta_repo_links': None,
'.meta_rt_links': ['MetaRTLinks'], '.meta_rt_links': ['MetaRTLinks'],
'.meta_tax_implication': None, '.meta_tax_implication': None,
'.meta_payroll_type': None, '.meta_payroll_type': [
'HealthInsuranceHook',
'OtherBenefitsHook',
'SalaryHook',
'TaxHook',
],
'.txn_date': ['TransactionDate'], '.txn_date': ['TransactionDate'],
} }

View file

@ -14,56 +14,86 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
from . import core
import datetime import datetime
from . import core
from .. import ranges from .. import ranges
from .. import data from .. import data
from .. import errors as errormod from .. import errors as errormod
from ..beancount_types import ( from ..beancount_types import (
Transaction, Transaction,
) )
class MetaPayrollType(core._PostingHook):
"""Verify payroll-type metadata, starting on 2020-03-01 (FY 2020), is provided for the accounts:
Expenses:Payroll:Salary, Expenses:Payroll:Taxes, Expenses:Payroll:Benefits:HealthInsurance, and
Expenses:Payroll:Benefits:Other.
Valid values for payroll-type are listed in the
_SALARY_TYPES, _TAX_TYPES, _HEALTH_INSURANCE_TYPES, and _OTHER_BENEFIT_TYPES
(respectively) in the class.
"""
METADATA_KEY = 'payroll-type' METADATA_KEY = 'payroll-type'
HOOK_GROUPS = frozenset(['metadata', METADATA_KEY])
_TAX_TYPES = [ "CA:PP", "CA:EI", class _PayrollTypeHook(core._NormalizePostingMetadataHook):
"US:SocialSecurity", "US:Medicare", ACCOUNT: str
"US:IL:Unemployment", VALUES_ENUM = core.MetadataEnum(METADATA_KEY, [])
"US:MA:Unemployment", "US:MA:WorkTrain", "US:MA:Health", TXN_DATE_RANGE = ranges.DateRange(
"US:OR:Unemployment" ] datetime.date(2020, 3, 1),
_SALARY_TYPES = [ "US:General", "US:PTO", "US:403b:Employee", "US:403b:Match", core.DEFAULT_STOP_DATE,
"CA:General", "CA:PTO", )
"CA:Taxes:Income", "CA:Taxes:EI", "CA:Taxes:PP",
"US:Taxes:Income", "US:Taxes:SocialSecurity", "US:Taxes:Medicare",
"US:IL:Taxes:Income",
"US:OR:Taxes:Income", "US:OR:Taxes:STT",
"US:MA:Taxes:Income", "US:MA:Disability:PML", "US:MA:Disability:PFL",
"US:NY:Taxes:Income", "US:NY:Disability:PFL", "US:NY:Disability", "US:NY:Taxes:NYC" ]
_HEALTH_INSURANCE_TYPES = [ "US:HRA:Fees", "US:HRA:Usage", "US:Premium:Main", "US:Premium:DentalVision" ]
_OTHER_BENEFIT_TYPES = [ "US:403b:Fees" ]
TXN_DATE_RANGE = ranges.DateRange(datetime.date(2020, 3, 1), core.DEFAULT_STOP_DATE)
def _run_on_post(self, txn: Transaction, post: data.Posting) -> bool: def _run_on_post(self, txn: Transaction, post: data.Posting) -> bool:
if post.account.is_under('Expenses:Payroll:Salary') or post.account.is_under('Expenses:Payroll:Taxes') or \ return post.account.is_under(self.ACCOUNT) is not None
post.account.is_under('Expenses:Payroll:Benefits:Other') or post.account.is_under('Expenses:Payroll:Benefits:HealthInsurance'):
return True
else:
return False
def post_run(self, txn: Transaction, post: data.Posting) -> errormod.Iter:
value = post.meta.get(self.METADATA_KEY) class HealthInsuranceHook(_PayrollTypeHook):
if value is None or \ ACCOUNT = 'Expenses:Payroll:Benefits:HealthInsurance'
(post.account.is_under('Expenses:Payroll:Salary') and (not value in self._SALARY_TYPES)) or \ VALUES_ENUM = core.MetadataEnum(METADATA_KEY, [
(post.account.is_under('Expenses:Payroll:Taxes') and (not value in self._TAX_TYPES)) or \ 'US:HRA:Fees',
(post.account.is_under('Expenses:Payroll:Benefits:HealthInsurance') and \ 'US:HRA:Usage',
(not value in self._HEALTH_INSURANCE_TYPES)) or \ 'US:Premium:DentalVision',
(post.account.is_under('Expenses:Payroll:Benefits:Other') and \ 'US:Premium:Main',
(not value in self._OTHER_BENEFIT_TYPES)): ])
yield errormod.InvalidMetadataError(txn, self.METADATA_KEY, value, post)
class OtherBenefitsHook(_PayrollTypeHook):
ACCOUNT = 'Expenses:Payroll:Benefits:Other'
VALUES_ENUM = core.MetadataEnum(METADATA_KEY, [
'US:403b:Fees',
])
class SalaryHook(_PayrollTypeHook):
ACCOUNT = 'Expenses:Payroll:Salary'
VALUES_ENUM = core.MetadataEnum(METADATA_KEY, [
'CA:General',
'CA:PTO',
'CA:Taxes:EI',
'CA:Taxes:Income',
'CA:Taxes:PP',
'US:403b:Employee',
'US:403b:Match',
'US:General',
'US:IL:Taxes:Income',
'US:MA:Disability:PFL',
'US:MA:Disability:PML',
'US:MA:Taxes:Income',
'US:NY:Disability',
'US:NY:Disability:PFL',
'US:NY:Taxes:Income',
'US:NY:Taxes:NYC',
'US:OR:Taxes:Income',
'US:OR:Taxes:STT',
'US:PTO',
'US:Taxes:Income',
'US:Taxes:Medicare',
'US:Taxes:SocialSecurity',
])
class TaxHook(_PayrollTypeHook):
ACCOUNT = 'Expenses:Payroll:Taxes'
VALUES_ENUM = core.MetadataEnum(METADATA_KEY, [
'CA:EI',
'CA:PP',
'US:IL:Unemployment',
'US:MA:Health',
'US:MA:Unemployment',
'US:MA:WorkTrain',
'US:Medicare',
'US:OR:Unemployment',
'US:SocialSecurity',
])

View file

@ -5,7 +5,7 @@ from setuptools import setup
setup( setup(
name='conservancy_beancount', name='conservancy_beancount',
description="Plugin, library, and reports for reading Conservancy's books", description="Plugin, library, and reports for reading Conservancy's books",
version='1.13.1', version='1.14.0',
author='Software Freedom Conservancy', author='Software Freedom Conservancy',
author_email='info@sfconservancy.org', author_email='info@sfconservancy.org',
license='GNU AGPLv3+', license='GNU AGPLv3+',