Commit 62f2af80 authored by Nicolas Wavrant's avatar Nicolas Wavrant

erp5_payroll_l10n_fr: can create DSN reports for organisations paying quaterly

parent 0926dc6f
from Products.ERP5Type.DateUtils import getIntervalBetweenDates, getNumberOfDayInMonth
from Products.ERP5Type.DateUtils import addToDate, getIntervalBetweenDates, getNumberOfDayInMonth
portal = context.getPortalObject()
portal_categories = context.portal_categories
......@@ -159,27 +159,32 @@ if block_id == 'S21.G00.15':
if block_id == 'S21.G00.20':
payment_transaction = target
bank_account = target.getSourcePaymentValue()
payment_source_section = payment_transaction.getSourceSectionValue()
payment_source_trade = payment_transaction.getSourceTradeValue()
amount = (kw['amount'] if 'amount' in kw else payment_transaction.AccountingTransactionLine_statSourceDebit())
if kw['establishment'] == payment_source_trade:
# A main establishment is paying for this one
rubric_value_dict['S21.G00.20.005'] = formatFloat(0.)
rubric_value_dict['S21.G00.20.010'] = '06'
rubric_value_dict['S21.G00.20.012'] = ''.join(payment_transaction.getSourceSectionValue().getCorporateRegistrationCode().split(' '))
elif kw['establishment'] == payment_transaction.getSourceSectionValue():
if payment_source_trade == kw['payer'] == kw['establishment']:
payment_mode = payment_transaction.getPaymentModeValue()
assert payment_mode is not None, "No payment mode defined on %s" % payment_transaction.absolute_url()
# Establishment pays for itself
rubric_value_dict['S21.G00.20.003'] = bank_account.getBicCode()
rubric_value_dict['S21.G00.20.004'] = bank_account.getIban()
rubric_value_dict['S21.G00.20.005'] = payment_transaction.AccountingTransactionLine_statSourceDebit()
rubric_value_dict['S21.G00.20.010'] = payment_transaction.getPaymentModeValue().getCodification()
if payment_source_trade is not None:
rubric_value_dict['S21.G00.20.003'] = ''.join(bank_account.getBicCode().split(' '))
rubric_value_dict['S21.G00.20.004'] = ''.join(bank_account.getIban().split(' '))
rubric_value_dict['S21.G00.20.005'] = amount
rubric_value_dict['S21.G00.20.010'] = payment_mode.getCodification()
if payment_source_trade is not None and payment_source_trade != payment_source_section:
# Establishment pays also for another one
rubric_value_dict['S21.G00.20.012'] = ''.join(kw['establishment'].getCorporateRegistrationCode().split(' '))
else:
# A main establishment is paying for this one
rubric_value_dict['S21.G00.20.005'] = formatFloat(0.)
rubric_value_dict['S21.G00.20.010'] = '06'
rubric_value_dict['S21.G00.20.012'] = ''.join(payment_source_section.getCorporateRegistrationCode().split(' '))
rubric_value_dict['S21.G00.20.001'] = kw['corporate_registration_code']
rubric_value_dict['S21.G00.20.002'] = ''.join(kw['establishment'].getCorporateRegistrationCode().split(' ')) # TODO: Check if it is always needed
rubric_value_dict['S21.G00.20.006'] = formatDate(payment_transaction.getStartDate()) # TODO: check simulation correctly sets it
rubric_value_dict['S21.G00.20.007'] = formatDate(payment_transaction.getStopDate())
rubric_value_dict['S21.G00.20.002'] = ''.join(kw['establishment'].getCorporateRegistrationCode().split(' '))
start_date = kw['first_date_of_month']
rubric_value_dict['S21.G00.20.006'] = formatDate(start_date)
rubric_value_dict['S21.G00.20.007'] = formatDate(kw['last_date_of_month'])
rubric_value_dict['S21.G00.20.008'] = ''
# Bordereau de cotisation due
......@@ -189,7 +194,7 @@ if block_id == 'S21.G00.22':
rubric_value_dict['S21.G00.22.002'] = ''.join(kw['establishment'].getCorporateRegistrationCode().split(' '))
rubric_value_dict['S21.G00.22.003'] = formatDate(kw['start_date'])
rubric_value_dict['S21.G00.22.004'] = formatDate(kw['stop_date'])
rubric_value_dict['S21.G00.22.005'] = payment_transaction.AccountingTransactionLine_statSourceDebit()
rubric_value_dict['S21.G00.22.005'] = formatFloat(kw['amount'])
if block_id == 'S21.G00.23':
rubric_value_dict['S21.G00.23.001'] = target['code'][:3]
......@@ -197,16 +202,17 @@ if block_id == 'S21.G00.23':
rubric_value_dict['S21.G00.23.003'] = ('' if not target['rate'] else formatFloat(target['rate']))
if target['quantity']:
assert target['quantity'] > 0
rubric_value_dict['S21.G00.23.005'] = formatFloat(target['quantity'])
rubric_value_dict['S21.G00.23.005'] = formatFloat(round(target['quantity']))
else:
rubric_value_dict['S21.G00.23.004'] = formatFloat(target['base'])
rubric_value_dict['S21.G00.23.004'] = formatFloat(round(target['base']))
rubric_value_dict['S21.G00.23.006'] = target['zip_code']
# Individu
if block_id == 'S21.G00.30':
birth_country_code = getCountryCode(target)
address = target.getDefaultAddressStreetAddress().strip().split('\n')
rubric_value_dict["S21.G00.30.001"] = "".join(target.getSocialCode('').split(' '))[:13]
social_code = target.getSocialCode('')
rubric_value_dict["S21.G00.30.001"] = ("" if not social_code else "".join(social_code.split(' '))[:13])
rubric_value_dict["S21.G00.30.002"] = target.getLastName()
rubric_value_dict["S21.G00.30.003"] = ''
rubric_value_dict["S21.G00.30.004"] = " ".join([target.getFirstName(), target.getMiddleName() or '']).strip()
......@@ -248,7 +254,10 @@ if block_id == 'S21.G00.40':
rubric_value_dict["S21.G00.40.016"] = enrollment_record.getLocalScheme()
rubric_value_dict["S21.G00.40.017"] = target.getCollectiveAgreementTitle()
rubric_value_dict["S21.G00.40.018"] = enrollment_record.getMedicalScheme()
rubric_value_dict["S21.G00.40.019"] = ''.join(target.getDestinationValue().getCorporateRegistrationCode().split(' '))[-5:]
try:
rubric_value_dict["S21.G00.40.019"] = ''.join(target.getDestinationValue().getCorporateRegistrationCode().split(' '))[-5:]
except AttributeError:
raise AttributeError(target, target.getDestinationValue())
rubric_value_dict["S21.G00.40.020"] = enrollment_record.getRetirementScheme()
rubric_value_dict["S21.G00.40.021"] = enrollment_record.getEnrollmentCausality()
rubric_value_dict["S21.G00.40.022"] = ''
......@@ -278,9 +287,9 @@ if block_id == 'S21.G00.40':
if block_id == 'S21.G00.50':
# target is a paysheet
rubric_value_dict['S21.G00.50.001'] = formatDate(context.getEffectiveDate())
rubric_value_dict['S21.G00.50.002'] = kw['net_taxable_salary']
rubric_value_dict['S21.G00.50.002'] = formatFloat(kw['net_taxable_salary'])
rubric_value_dict['S21.G00.50.003'] = ''
rubric_value_dict['S21.G00.50.004'] = kw['net_salary']
rubric_value_dict['S21.G00.50.004'] = formatFloat(kw['net_salary'])
if block_id == 'S21.G00.52':
rubric_value_dict['S21.G00.52.001'] = target['code']
......@@ -304,14 +313,14 @@ if block_id == 'S21.G00.55':
if corporate_registration_code not in ('ORGANISATION1', 'ORGANISATION2'):
return {}
payment_source_trade = target.getSourceTradeValue()
if kw['establishment'] == payment_source_trade:
rubric_value_dict['S21.G00.55.001'] = formatFloat(0.)
elif kw['establishment'] == target.getSourceSectionValue():
if kw['establishment'] == target.getSourceSectionValue():
rubric_value_dict['S21.G00.55.001'] = target.AccountingTransactionLine_statSourceDebit()
else:
rubric_value_dict['S21.G00.55.001'] = formatFloat(0.)
rubric_value_dict['S21.G00.55.002'] = ''
rubric_value_dict['S21.G00.55.003'] = 'REF_CONTRACT' + corporate_registration_code[-1]
rubric_value_dict['S21.G00.55.004'] = getPaymentPeriod(target.getStopDate(), 'M')
rubric_value_dict['S21.G00.55.004'] = getPaymentPeriod(context.getEffectiveDate(), 'M')
# Fin du contrat
if block_id == 'S21.G00.62':
......@@ -341,20 +350,23 @@ if block_id == 'S21.G00.65':
# Affiliation Prevoyance
if block_id == 'S21.G00.70':
if enrollment_record.getContractType() == '29':
return rubric_value_dict
# XXX: Hack as some organisations may have several contracts
return [
{
if kw['contract_id'] == '1':
return {
'S21.G00.70.004': 'Option1',
'S21.G00.70.005': '',
'S21.G00.70.012': '1',
'S21.G00.70.013': '1',
},
{
}
elif kw['contract_id'] == '2':
return {
'S21.G00.70.004': 'Option2',
'S21.G00.70.005': '1',
'S21.G00.70.012': '2',
'S21.G00.70.013': '2',
}]
}
# Retraite complementaire
if block_id == 'S21.G00.71':
......@@ -394,13 +406,13 @@ if block_id == 'S21.G00.86':
seniority = getIntervalBetweenDates(career_start_date, DateTime())
if seniority['year'] != 0:
rubric_value_dict['S21.G00.86.002'] = '03'
rubric_value_dict['S21.G00.86.003'] = seniority['year']
rubric_value_dict['S21.G00.86.003'] = int(seniority['year'])
elif seniority['month'] != 0:
rubric_value_dict['S21.G00.86.002'] = '02'
rubric_value_dict['S21.G00.86.003'] = seniority['month']
rubric_value_dict['S21.G00.86.003'] = int(seniority['month'])
elif seniority['day'] != 0:
rubric_value_dict['S21.G00.86.002'] = '01'
rubric_value_dict['S21.G00.86.003'] = seniority['day']
rubric_value_dict['S21.G00.86.003'] = int(seniority['day'])
rubric_value_dict['S21.G00.86.001'] = '01'
rubric_value_dict['S21.G00.86.005'] = '00000'
......
......@@ -13,16 +13,10 @@ result = portal.portal_catalog(portal_type="DSN Monthly Report",
simulation_state="validated",
sort_on=[("creation_date", "descending")])
if len(result) != 0:
# if there is a previous DSN, we report leave requests from last end-of-pay date
last_dsn = result[0].getObject()
from_date = last_dsn.getEffectiveDate()
else:
# else we get 1st day of current month
from_date = DateTime(effective_date.year(), effective_date.month(), 1)
from_date = DateTime(effective_date.year(), effective_date.month(), 1)
# We report leave periods which are not over yet ...
result = portal.portal_catalog(query=SimpleQuery(expiration_date=None), portal_type='Leave Request Period')
result = portal.portal_catalog(SimpleQuery(expiration_date=None), portal_type='Leave Request Period')
leave_period_list = [period.getObject() for period in result]
# ... And leave periods which ended during last period
......@@ -34,9 +28,9 @@ leave_period_list.extend([period.getObject() for period in result if period.getE
def formatDate(datetime):
return "%02d%02d%04d" % (datetime.day(), datetime.month(), datetime.year())
def getLeaveBlocAsDict(leave_period):
def getLeaveBlocAsDict(leave_period, leave_category):
bloc = {}
bloc['S21.G00.60.001'] = leave_period.getResourceValue().getCodification()
bloc['S21.G00.60.001'] = leave_category.getCodification()
bloc['S21.G00.60.002'] = formatDate(leave_period.getStartDate())
bloc['S21.G00.60.003'] = formatDate(leave_period.getStopDate())
# employee left during this period
......@@ -46,7 +40,6 @@ def getLeaveBlocAsDict(leave_period):
bloc['S21.G00.60.005'] = formatDate(first_subrogation_day)
# 3 months of subrogation, as defined in the collective agreement
bloc['S21.G00.60.006'] = formatDate(addToDate(first_subrogation_day, month=3, days=-1))
bank_account = payment_transaction.getSourcePaymentValue()
bloc['S21.G00.60.007'] = bank_account.getIban()
bloc['S21.G00.60.008'] = bank_account.getBicCode()
else:
......@@ -57,18 +50,22 @@ def getLeaveBlocAsDict(leave_period):
bloc['S21.G00.60.011'] = '01' # Restart normally
return bloc
leave_period_type_list = portal_categories.calendar_period_type.social_declaration.l10n.fr.getCategoryChildValueList()
leave_period_type_set = set(portal_categories.use.social_declaration.l10n.fr.leave.getCategoryChildValueList())
# Create dict containing a DSN leave blocs, grouped by employee
leave_dict = {}
for period in leave_period_list:
# some leave periods don't have to be reported in DSN
if period.getResourceValue() not in leave_period_type_list:
period_resource = period.getResourceValue()
assert period_resource is not None, 'No type set on Leave Request %s' % period.absolute_url()
leave_category = set(period.getResourceValue().getUseValueList()).intersection(leave_period_type_set)
if not leave_category:
continue
# Let's make a DSN Bloc for this leave period
leave_category = leave_category.pop()
if period.getDestinationValue() in leave_dict.keys():
leave_dict[period.getDestination()].append(getLeaveBlocAsDict(period))
leave_dict[period.getDestination()].append(getLeaveBlocAsDict(period, leave_category))
else:
leave_dict[period.getDestination()] = [getLeaveBlocAsDict(period),]
leave_dict[period.getDestination()] = [getLeaveBlocAsDict(period, leave_category),]
return leave_dict
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>payment_transaction</string> </value>
<value> <string>bank_account</string> </value>
</item>
<item>
<key> <string>id</string> </key>
......
......@@ -24,6 +24,7 @@ all_taxable_base_set = set(portal_categories.getCategoryValue('base_amount/payro
all_taxable_base_component_set = set(portal_categories.getCategoryValue('base_amount/payroll/l10n/fr/taxable_base_component').objectValues(portal_type='Category'))
all_other_income_set = set(portal_categories.getCategoryValue('base_amount/payroll/l10n/fr/other_income').objectValues(portal_type='Category'))
all_other_bonus_set = set(portal_categories.getCategoryValue('base_amount/payroll/l10n/fr/other_bonus').objectValues(portal_type='Category'))
trainee_base_contribution = portal_categories.getCategoryValue('base_amount/payroll/l10n/fr/base/gratification_stage')
def formatDate(datetime):
return "%02d%02d%04d" % (datetime.day(), datetime.month(), datetime.year())
......@@ -54,17 +55,20 @@ ZIP_CODE = context.getDestinationSectionValue().getDefaultAddressZipCode()
INSEE_CODE = getINSEECode(ZIP_CODE)
def makeCTPBlock(movement, category):
return {
'code': category,
'corporate_registration_code': movement.getSourceSectionValue().getCorporateRegistrationCode(),
'cap': ('921' if category[-1] == 'P' else '920'),
'rate': (abs(getattr(movement, 'employer_price') * 100) if category in ('100A', '900D', '901D', '863A') else ''),
'base': round(movement.base),
'quantity': ((getattr(movement, 'employer_total_price', 0.) + getattr(movement, 'employee_total_price', 0.)) if category[:3] in ('437', '671') else ''),
'zip_code': (INSEE_CODE if category == '900T' else ''),
'start_date': movement.getStartDate(),
'stop_date': movement.getStopDate(),
}
quantity = 0.
if category[:3] in ('437', '671'):
quantity = getattr(movement, 'employer_total_price', 0.) + getattr(movement, 'employee_total_price', 0.)
return {
'code': category,
'corporate_registration_code': movement.getSourceSectionValue(),
'cap': ('921' if category[-1] == 'P' else '920'),
'rate': (abs(getattr(movement, 'employer_price') * 100) if category in ('100A', '900D', '901D', '863A') else ''),
'base': movement.base,
'quantity': quantity,
'zip_code': (INSEE_CODE if category == '900T' else ''),
'start_date': movement.getStartDate(),
'stop_date': movement.getStopDate(),
}
def makeTaxableBaseBlock(movement, category):
return {
......@@ -143,6 +147,7 @@ for movement in context.PaySheetTransaction_getMovementList():
contribution_dict = makeCTPBlock(movement, category)
if category in result["ctp"]:
result['ctp'][category]['base'] = result['ctp'][category]['base'] + contribution_dict['base']
result['ctp'][category]['quantity'] = result['ctp'][category]['quantity'] + contribution_dict['quantity']
else:
result['ctp'][category] = contribution_dict
......@@ -178,22 +183,67 @@ for movement in context.PaySheetTransaction_getMovementList():
for category in other_income_set:
category = category.getCodification()
contribution_dict = makeOtherIncomeBlock(movement, category)
if category in result["other_income"]:
result['other_income'][category]['base'] = result['other_income'][category]['base'] + contribution_dict['base']
else:
result['other_income'][category] = contribution_dict
if contribution_dict['quantity']:
if category in result["other_income"]:
result['other_income'][category]['quantity'] = result['other_income'][category]['quantity'] + contribution_dict['quantity']
else:
result['other_income'][category] = contribution_dict
other_bonus_set = all_other_bonus_set.intersection(contribution_set)
total_bonus = 0.0
for category in other_bonus_set:
category = category.getCodification()
contribution_dict = makeOtherBonusBlock(movement, category)
if category in result["other_bonus"]:
result['other_bonus'][category]['base'] = result['other_bonus'][category]['base'] + contribution_dict['base']
else:
result['other_bonus'][category] = contribution_dict
total_bonus += contribution_dict['quantity']
if contribution_dict['quantity']:
if category in result["other_bonus"]:
result['other_bonus'][category]['quantity'] = result['other_bonus'][category]['quantity'] + contribution_dict['quantity']
else:
result['other_bonus'][category] = contribution_dict
total_bonus += contribution_dict['quantity']
if trainee_base_contribution in contribution_set:
trainee_bonus = movement.base
result['taxable_base'][('02', '')] = {
'code': '02',
'start_date': '',
'stop_date': '',
'base': 0.,
'contract_id': ''
}
result['taxable_base'][('03', '')] = {
'code': '03',
'start_date': '',
'stop_date': '',
'base': 0.,
'contract_id': ''
}
result['individual_contribution'][('022', '')] = {
'code': '022',
'corporate_registration_code': '',
'base': trainee_bonus,
'quantity': 0.,
'zip_code': '',
'contract_id': '',
}
# Let's try to calculate CTP 400D, which doesn't appear in the paysheet
if len(result['ctp']):
year_to_date_gross_salary = float(other_information_data_dict['year_to_date_gross_salary'])
try:
minimum_salary = float(context.getRatioQuantityFromReference('salaire_minimum_mensuel'))
except:
raise AttributeError(context.getUrl())
if year_to_date_gross_salary < 2.5 * minimum_salary * int(context.getStopDate().month()):
category = '400D'
result['ctp'][category] = {
'code': category,
'cap': ('921' if category[-1] == 'P' else '920'),
'rate': '',
'base': year_to_date_gross_salary,
'quantity': 0,
'zip_code': ''
}
######################################################################
# Remuneration and Activity
......@@ -210,6 +260,8 @@ def getRemunerationBlockAsDict(remuneration_type, amount):
# Corporate executives and trainees don't contribute to unemployment fee
if is_corporate_executive and remuneration_type == '002':
amount = 0.
elif is_trainee and remuneration_type == '001':
amount = trainee_bonus
elif is_trainee and remuneration_type == '002':
amount = 0.
# Nexedi trainees don't pay social fees
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment