Merge pull request #517 from acsone/11.0-expand-pain-support-sbi

[11.0] expand pain support
This commit is contained in:
Stéphane Bidoul (ACSONE)
2018-11-07 16:20:19 +01:00
committed by GitHub
11 changed files with 176 additions and 12 deletions

View File

@@ -55,6 +55,111 @@ class AccountPaymentLine(models.Model):
], string="Category Purpose",
help="If neither your bank nor your local regulations oblige you to "
"set the category purpose, leave the field empty.")
purpose = fields.Selection(
# Full category purpose list found on:
# https://www.iso20022.org/external_code_list.page
# Document "External Code Sets spreadsheet" version 31 August, 2018
selection=[
('ACCT', 'Account Management'),
('CASH', 'Cash Management Transfer'),
('COLL', 'Collection Payment'),
('INTC', 'Intra Company Payment'),
('LIMA', 'Liquidity Management'),
('NETT', 'Netting'),
('AGRT', 'Agricultural Transfer'),
('BEXP', 'Business Expenses'),
('COMC', 'Commercial Payment'),
('CPYR', 'Copyright'),
('GDDS', 'Purchase Sale Of Goods'),
('LICF', 'License Fee'),
('ROYA', 'Royalties'),
('SCVE', 'Purchase Sale Of Services'),
('SUBS', 'Subscription'),
('SUPP', 'Supplier Payment'),
('TRAD', 'Trade Services'),
('CHAR', 'Charity Payment'),
('COMT', 'Consumer Third Party Consolidated Payment'),
('CLPR', 'Car Loan Principal Repayment'),
('GOVI', 'Government Insurance'),
('HLRP', 'Housing Loan Repayment'),
('INSU', 'Insurance Premium'),
('INTE', 'Interest'),
('LBRI', 'Labor Insurance'),
('LIFI', 'Life Insurance'),
('LOAN', 'Loan'),
('LOAR', 'Loan Repayment'),
('PPTI', 'Property Insurance'),
('RINP', 'Recurring Installment Payment'),
('TRFD', 'Trust Fund'),
('ADVA', 'Advance Payment'),
('CCRD', 'Credit Card Payment '),
('CFEE', 'Cancellation Fee'),
('COST', 'Costs'),
('DCRD', 'Debit Card Payment'),
('GOVT', 'Government Payment'),
('IHRP', 'Instalment Hire Purchase Agreement'),
('INSM', 'Installment'),
('MSVC', 'Multiple Service Types'),
('NOWS', 'Not Otherwise Specified'),
('OFEE', 'Opening Fee'),
('OTHR', 'Other'),
('PADD', 'Preauthorized debit'),
('PTSP', 'Payment Terms'),
('RCPT', 'Receipt Payment'),
('RENT', 'Rent'),
('STDY', 'Study'),
('ANNI', 'Annuity'),
('CMDT', 'Commodity Transfer'),
('DERI', 'Derivatives'),
('DIVD', 'Dividend'),
('FREX', 'Foreign Exchange'),
('HEDG', 'Hedging'),
('PRME', 'Precious Metal'),
('SAVG', 'Savings'),
('SECU', 'Securities'),
('TREA', 'Treasury Payment'),
('ANTS', 'Anesthesia Services'),
('CVCF', 'Convalescent Care Facility'),
('DMEQ', 'Durable Medicale Equipment'),
('DNTS', 'Dental Services'),
('HLTC', 'Home Health Care'),
('HLTI', 'Health Insurance'),
('HSPC', 'Hospital Care'),
('ICRF', 'Intermediate Care Facility'),
('LTCF', 'Long Term Care Facility'),
('MDCS', 'Medical Services'),
('VIEW', 'Vision Care'),
('ALMY', 'Alimony Payment'),
('BECH', 'Child Benefit'),
('BENE', 'Unemployment Disability Benefit'),
('BONU', 'Bonus Payment.'),
('COMM', 'Commission'),
('PENS', 'Pension Payment'),
('PRCP', 'Price Payment'),
('SALA', 'Salary Payment'),
('SSBE', 'Social Security Benefit'),
('ESTX', 'Estate Tax'),
('HSTX', 'Housing Tax'),
('INTX', 'Income Tax'),
('TAXS', 'Tax Payment'),
('VATX', 'Value Added Tax Payment'),
('AIRB', 'Air'),
('BUSB', 'Bus'),
('FERB', 'Ferry'),
('RLWY', 'Railway'),
('CBTV', 'Cable TV Bill'),
('ELEC', 'Electricity Bill'),
('ENRG', 'Energies'),
('GASB', 'Gas Bill'),
('NWCH', 'Network Charge'),
('NWCM', 'Network Communication'),
('OTLC', 'Other Telecom Related Bill'),
('PHON', 'Telephone Bill'),
('WTER', 'Water Bill'),
],
help="If neither your bank nor your local regulations oblige you to "
"set the category purpose, leave the field empty.",
)
# PAIN allows 140 characters
communication = fields.Char(size=140)
# The field struct_communication_type has been dropped in v9

View File

@@ -27,3 +27,9 @@ class AccountPaymentMode(models.Model):
"- Country code (2, optional)\n"
"- Company idenfier (N, VAT)\n"
"- Service suffix (N, issued by bank)")
initiating_party_scheme = fields.Char(
string='Initiating Party Scheme', size=35,
help="This will be used as the 'Initiating Party Scheme Name' in "
"the PAIN files generated by Odoo. This value is determined by the "
"financial institution that will process the file. If not defined, "
"no scheme will be used.\n")

View File

@@ -284,6 +284,9 @@ class AccountPaymentOrder(models.Model):
initiating_party_issuer = (
self.payment_mode_id.initiating_party_issuer or
self.payment_mode_id.company_id.initiating_party_issuer)
initiating_party_scheme = (
self.payment_mode_id.initiating_party_scheme or
self.payment_mode_id.company_id.initiating_party_scheme)
# in pain.008.001.02.ch.01.xsd files they use
# initiating_party_identifier but not initiating_party_issuer
if initiating_party_identifier:
@@ -292,6 +295,12 @@ class AccountPaymentOrder(models.Model):
iniparty_org_other = etree.SubElement(iniparty_org_id, 'Othr')
iniparty_org_other_id = etree.SubElement(iniparty_org_other, 'Id')
iniparty_org_other_id.text = initiating_party_identifier
if initiating_party_scheme:
iniparty_org_other_scheme = etree.SubElement(
iniparty_org_other, 'SchmeNm')
iniparty_org_other_scheme_name = etree.SubElement(
iniparty_org_other_scheme, 'Prtry')
iniparty_org_other_scheme_name.text = initiating_party_scheme
if initiating_party_issuer:
iniparty_org_other_issuer = etree.SubElement(
iniparty_org_other, 'Issr')
@@ -341,6 +350,16 @@ class AccountPaymentOrder(models.Model):
# as per the guidelines of the EPC
return True
@api.model
def generate_party_id(
self, parent_node, party_type, partner):
"""Generate an Id element for partner inside the parent node.
party_type can currently be Cdtr or Dbtr. Notably, the initiating
party orgid is generated with another mechanism and configured
at the company or payment mode level.
"""
return
@api.model
def generate_party_acc_number(
self, parent_node, party_type, order, partner_bank, gen_args,
@@ -390,6 +409,19 @@ class AccountPaymentOrder(models.Model):
partner = partner_bank.partner_id
if partner.country_id:
postal_address = etree.SubElement(party, 'PstlAdr')
if gen_args.get('pain_flavor').startswith(
'pain.001.001.') or gen_args.get('pain_flavor').startswith(
'pain.008.001.'):
if partner.zip:
pstcd = etree.SubElement(postal_address, 'PstCd')
pstcd.text = self._prepare_field(
'Postal Code', 'partner.zip',
{'partner': partner}, 16, gen_args=gen_args)
if partner.city:
twnnm = etree.SubElement(postal_address, 'TwnNm')
twnnm.text = self._prepare_field(
'Town Name', 'partner.city',
{'partner': partner}, 35, gen_args=gen_args)
country = etree.SubElement(postal_address, 'Ctry')
country.text = self._prepare_field(
'Country', 'partner.country_id.code',
@@ -399,11 +431,8 @@ class AccountPaymentOrder(models.Model):
adrline1.text = self._prepare_field(
'Adress Line1', 'partner.street',
{'partner': partner}, 70, gen_args=gen_args)
if partner.city and partner.zip:
adrline2 = etree.SubElement(postal_address, 'AdrLine')
adrline2.text = self._prepare_field(
'Address Line2', "partner.zip + ' ' + partner.city",
{'partner': partner}, 70, gen_args=gen_args)
self.generate_party_id(party, party_type, partner)
self.generate_party_acc_number(
parent_node, party_type, order, partner_bank, gen_args,

View File

@@ -14,10 +14,12 @@ class BankPaymentLine(models.Model):
string='Local Instrument')
category_purpose = fields.Selection(
related='payment_line_ids.category_purpose', string='Category Purpose')
purpose = fields.Selection(
related='payment_line_ids.purpose')
@api.model
def same_fields_payment_line_and_bank_payment_line(self):
res = super(BankPaymentLine, self).\
same_fields_payment_line_and_bank_payment_line()
res += ['priority', 'local_instrument', 'category_purpose']
res += ['priority', 'local_instrument', 'category_purpose', 'purpose']
return res

View File

@@ -20,6 +20,10 @@ class ResCompany(models.Model):
string='Initiating Party Identifier', size=35,
help="This will be used as the 'Initiating Party Identifier' in "
"the PAIN files generated by Odoo.")
initiating_party_scheme = fields.Char(
string='Initiating Party Scheme', size=35,
help="This will be used as the 'Initiating Party Scheme Name' in "
"the PAIN files generated by Odoo.")
def _default_initiating_party(self):
'''This method is called from post_install.py'''

View File

@@ -12,6 +12,8 @@ class ResConfigSettings(models.TransientModel):
related='company_id.initiating_party_issuer')
initiating_party_identifier = fields.Char(
related='company_id.initiating_party_identifier')
initiating_party_scheme = fields.Char(
related='company_id.initiating_party_scheme')
group_pain_multiple_identifier = fields.Boolean(
string='Multiple identifiers',
implied_group='account_banking_pain_base.'

View File

@@ -15,6 +15,7 @@
<field name="priority"/>
<field name="local_instrument"/>
<field name="category_purpose"/>
<field name="purpose"/>
</field>
</field>
</record>

View File

@@ -15,6 +15,7 @@
<group name="main" position="inside">
<field name="initiating_party_identifier" groups="account_banking_pain_base.group_pain_multiple_identifier"/>
<field name="initiating_party_issuer" groups="account_banking_pain_base.group_pain_multiple_identifier"/>
<field name="initiating_party_scheme" groups="account_banking_pain_base.group_pain_multiple_identifier"/>
</group>
</field>
</record>

View File

@@ -15,6 +15,7 @@
<field name="priority"/>
<field name="local_instrument"/>
<field name="category_purpose"/>
<field name="purpose"/>
</field>
</field>
</record>

View File

@@ -130,6 +130,11 @@ class AccountPaymentOrder(models.Model):
payment_info, 'CdtTrfTxInf')
payment_identification = etree.SubElement(
credit_transfer_transaction_info, 'PmtId')
instruction_identification = etree.SubElement(
payment_identification, 'InstrId')
instruction_identification.text = self._prepare_field(
'Instruction Identification', 'line.name',
{'line': line}, 35, gen_args=gen_args)
end2end_identification = etree.SubElement(
payment_identification, 'EndToEndId')
end2end_identification.text = self._prepare_field(
@@ -153,6 +158,10 @@ class AccountPaymentOrder(models.Model):
self.generate_party_block(
credit_transfer_transaction_info, 'Cdtr',
'C', line.partner_bank_id, gen_args, line)
if line.purpose:
purpose = etree.SubElement(
credit_transfer_transaction_info, 'Purp')
etree.SubElement(purpose, 'Cd').text = line.purpose
self.generate_remittance_info_block(
credit_transfer_transaction_info, line, gen_args)
if not pain_flavor.startswith('pain.001.001.02'):

View File

@@ -156,12 +156,11 @@ class AccountPaymentOrder(models.Model):
payment_info, 'DrctDbtTxInf')
payment_identification = etree.SubElement(
dd_transaction_info, 'PmtId')
if pain_flavor == 'pain.008.001.02.ch.01':
instruction_identification = etree.SubElement(
payment_identification, 'InstrId')
instruction_identification.text = self._prepare_field(
'Intruction Identification', 'line.name',
{'line': line}, 35, gen_args=gen_args)
instruction_identification = etree.SubElement(
payment_identification, 'InstrId')
instruction_identification.text = self._prepare_field(
'Instruction Identification', 'line.name',
{'line': line}, 35, gen_args=gen_args)
end2end_identification = etree.SubElement(
payment_identification, 'EndToEndId')
end2end_identification.text = self._prepare_field(
@@ -215,6 +214,11 @@ class AccountPaymentOrder(models.Model):
dd_transaction_info, 'Dbtr', 'C',
line.partner_bank_id, gen_args, line)
if line.purpose:
purpose = etree.SubElement(
dd_transaction_info, 'Purp')
etree.SubElement(purpose, 'Cd').text = line.purpose
self.generate_remittance_info_block(
dd_transaction_info, line, gen_args)