/
procXlsx.py
281 lines (243 loc) · 9.77 KB
/
procXlsx.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
import openpyxl
import datetime
import os
import lxml.etree as etree
def procXL(zip_path, xlsx_file):
workbook = openpyxl.load_workbook(filename=xlsx_file, data_only=True)
# check that the required sheets are in the Excel File
setXLFiles = set(workbook.sheetnames)
if not {'Header Record', 'Payment Information Record', 'Credit Instruction Record', 'Control',
'Control Data (Hidden)'}.issubset(setXLFiles):
critical_err = 'The XL File is not structured properly'
print(critical_err)
input('Press Enter to terminate.')
raise Exception(critical_err)
# Build the XML document
nsmap = {
'xsi': "http://www.w3.org/2001/XMLSchema-instance",
None: "urn:iso:std:iso:20022:tech:xsd:pain.001.001.03"
}
root = etree.Element('Document', nsmap=nsmap)
# Fill in the Computed MsgId and PmtInfld to (necessary if the user leaves these blank)
sh = workbook['Control Data (Hidden)']
computedMsgId = sh['B18'].value
computedPmtInfld = sh['B20'].value
CstmrCdtTrfInitn = etree.SubElement(root, 'CstmrCdtTrfInitn')
# Header Record
CstmrCdtTrfInitn = bldHeader(CstmrCdtTrfInitn, computedMsgId, workbook)
# This tag covers both PIR and CIR sections
PmtInf = etree.SubElement(CstmrCdtTrfInitn, "PmtInf")
# Payment Information Record
PmtInf = bldPIR(PmtInf, computedPmtInfld, workbook)
# Credit Instruction Record
PmtInf = bldCIR(PmtInf, workbook)
datastr = etree.tostring(root, xml_declaration=True, encoding='utf-8', pretty_print=True)
# Get the name of the XML file that will store the transactions
sh = workbook['Control']
xmlFile = sh['B2'].value
if xmlFile is None or xmlFile.strip() == '':
critical_err = 'Invalid SCT file name'
print(critical_err)
input('Press Enter to terminate.')
raise Exception(critical_err)
srcFile = xmlFile.strip() + ".SCT"
# fileSCT = os.path.join(tempdir, srcFile) - write file to final directory rather than tempdir
fileSCT = os.path.join(zip_path, srcFile)
try:
with open(fileSCT, 'wb') as file:
file.write(datastr)
except:
critical_err = 'Unable to create SCT file'
print('\n\n' + critical_err + '\n\n')
input('Press Enter to terminate.')
raise Exception(critical_err)
def bldCIR(PmtInf, workbook):
sh = workbook['Credit Instruction Record']
row = 5
lstEndToEndId = []
# list stores the sEndToEndId values. If there are duplicate entries raises an exception - ACB 202309
while row < 200:
sInstrId = sh['A' + str(row)].value
if sInstrId is None:
break
result = bldCIRrow(sh, PmtInf, workbook, row)
PmtInf = result[0]
sEndToEndId = result[1]
if sEndToEndId in lstEndToEndId:
critical_err = 'EndToEndId {0} has been already used in this batch'.format(sEndToEndId)
print('\n\n' + critical_err+ '\n\n')
input('Press Enter to terminate.')
raise Exception(critical_err)
else:
lstEndToEndId.append(sEndToEndId)
row += 2
return PmtInf
def bldCIRrow(sh, PmtInf, workbook, row):
# Process the particular row
# Read the Fields from this worksheet
sInstrId = sh['A' + str(row)].value.strip()
sEndToEndId = sh['B' + str(row)].value.strip()
sCcy = sh['C' + str(row)].value.strip()
sInstdAmt = '{0:.2f}'.format(sh['D' + str(row)].value)
sBIC = sh['E' + str(row)].value.strip()
sNm = sh['F' + str(row)].value.strip()
sAdrLine1 = sh['G5'].value
if sAdrLine1 is None:
sAdrLine1 = ''
else:
sAdrLine1 = sAdrLine1.strip()
sAdrLine2 = sh['H5'].value
if sAdrLine2 is None:
sAdrLine2 = ''
else:
sAdrLine2 = sAdrLine2.strip()
if sAdrLine1 == '':
sAdrLine1 = sAdrLine2
sIBAN = sh['I' + str(row)].value.strip()
sCd = sh['J' + str(row)].value.strip()
sUstrd = sh['K' + str(row)].value.strip()
CdtTrfTxInf = etree.SubElement(PmtInf, "CdtTrfTxInf")
PmtId = etree.SubElement(CdtTrfTxInf, "PmtId")
InstrId = etree.SubElement(PmtId, "InstrId")
InstrId.text = sInstrId
EndToEndId = etree.SubElement(PmtId, "EndToEndId")
EndToEndId.text = sEndToEndId
Amt = etree.SubElement(CdtTrfTxInf, "Amt")
InstdAmt = etree.SubElement(Amt, "InstdAmt")
InstdAmt.set('Ccy', sCcy)
InstdAmt.text = sInstdAmt
CdtrAgt = etree.SubElement(CdtTrfTxInf, "CdtrAgt")
FinInstnId = etree.SubElement(CdtrAgt, "FinInstnId")
BIC = etree.SubElement(FinInstnId, "BIC")
BIC.text = sBIC
Cdtr = etree.SubElement(CdtTrfTxInf, "Cdtr")
Nm = etree.SubElement(Cdtr, "Nm")
Nm.text = sNm
# Only fill in the subnodes if the address lines are not blank
if sAdrLine1 != "":
PstlAdr = etree.SubElement(Cdtr, "PstlAdr")
AdrLine1 = etree.SubElement(PstlAdr, "AdrLine")
AdrLine1.text = sAdrLine1
AdrLine2 = etree.SubElement(PstlAdr, "AdrLine")
AdrLine2.text = sAdrLine2
CdtrAcct = etree.SubElement(CdtTrfTxInf, "CdtrAcct")
Id = etree.SubElement(CdtrAcct, "Id")
IBAN = etree.SubElement(Id, "IBAN")
IBAN.text = sIBAN
Purp = etree.SubElement(CdtTrfTxInf, "Purp")
Cd = etree.SubElement(Purp, "Cd")
Cd.text = sCd
RmtInf = etree.SubElement(CdtTrfTxInf, "RmtInf")
Ustrd = etree.SubElement(RmtInf, "Ustrd")
Ustrd.text = sUstrd
return PmtInf, sEndToEndId
def bldPIR(PmtInf, computedPmtInfld, workbook):
sh = workbook['Payment Information Record']
# Read the Fields from this worksheet
sPmtInfId = sh['A5'].value
if sPmtInfId is None:
sPmtInfId = computedPmtInfld
sPmtInfId = sPmtInfId.strip()
# Cechk for a space condition
if sPmtInfId == '':
sPmtInfId = computedPmtInfld.strip()
sPmtMtd = sh['B5'].value.strip()
sBtchBookg = sh['C5'].value.strip()
sNbOfTxs = str(int(sh['D5'].value))
sCtrlSum = '{0:.2f}'.format(sh['E5'].value)
sCd = sh['F5'].value.strip()
sReqdExctnDt = sh['G5'].value
sReqdExctnDt = datetime.datetime.strftime(sReqdExctnDt, '%Y-%m-%d')
sNm = sh['H5'].value.strip()
sAdrLine1 = sh['I5'].value
if sAdrLine1 is None:
sAdrLine1 = ''
else:
sAdrLine1 = sAdrLine1.strip()
sAdrLine2 = sh['J5'].value
if sAdrLine2 is None:
sAdrLine2 = ''
else:
sAdrLine2 = sAdrLine2.strip()
if sAdrLine1 == '':
sAdrLine1 = sAdrLine2
sIBAN = sh['K5'].value.strip()
sCcy = sh['L5'].value.strip()
sBIC = sh['M5'].value.strip()
PmtInfId = etree.SubElement(PmtInf, "PmtInfId")
PmtInfId.text = sPmtInfId
PmtMtd = etree.SubElement(PmtInf, "PmtMtd")
PmtMtd.text = sPmtMtd
BtchBookg = etree.SubElement(PmtInf, "BtchBookg")
BtchBookg.text = sBtchBookg
NbOfTxs = etree.SubElement(PmtInf, "NbOfTxs")
NbOfTxs.text = sNbOfTxs
CtrlSum = etree.SubElement(PmtInf, "CtrlSum")
CtrlSum.text = sCtrlSum
PmtTpInf = etree.SubElement(PmtInf, "PmtTpInf")
SvcLvl = etree.SubElement(PmtTpInf, "SvcLvl")
Cd = etree.SubElement(SvcLvl, "Cd")
Cd.text = sCd
ReqdExctnDt = etree.SubElement(PmtInf, "ReqdExctnDt")
ReqdExctnDt.text = sReqdExctnDt
Dbtr = etree.SubElement(PmtInf, "Dbtr")
Nm = etree.SubElement(Dbtr, "Nm")
Nm.text = sNm
# Only fill in the subnodes if the address lines are not blank
if sAdrLine1 != "":
PstlAdr = etree.SubElement(Dbtr, "PstlAdr")
AdrLine1 = etree.SubElement(PstlAdr, "AdrLine")
AdrLine1.text = sAdrLine1
# Only fill if Address line 2 is not null
if sAdrLine2 != "":
AdrLine2 = etree.SubElement(PstlAdr, "AdrLine")
AdrLine2.text = sAdrLine2
DbtrAcct = etree.SubElement(PmtInf, "DbtrAcct")
Id = etree.SubElement(DbtrAcct, "Id")
IBAN = etree.SubElement(Id, "IBAN")
IBAN.text = sIBAN
Ccy = etree.SubElement(DbtrAcct, "Ccy")
Ccy.text = sCcy
DbtrAgt = etree.SubElement(PmtInf, "DbtrAgt")
FinInstnId = etree.SubElement(DbtrAgt, "FinInstnId")
BIC = etree.SubElement(FinInstnId, "BIC")
BIC.text = sBIC
return PmtInf
def bldHeader(CstmrCdtTrfInitn, computedMsgId, workbook):
sh = workbook['Header Record']
# Read the Fields from this worksheet
sMsgId = sh['A5'].value
if sMsgId is None:
sMsgId = computedMsgId
sMsgId = sMsgId.strip()
# check for a space condition
if sMsgId == '':
sMsgId = computedMsgId.strip()
sCreDtTm = str(sh['B5'].value)
# cater for different formats with microseconds and without
try:
sCreDtTm = datetime.datetime.strptime(sCreDtTm, "%Y-%m-%d %H:%M:%S.%f").replace(microsecond=0).isoformat()
except:
sCreDtTm = datetime.datetime.strptime(sCreDtTm, "%Y-%m-%d %H:%M:%S").isoformat()
sNbOfTxs = str(int(sh['C5'].value))
sCtrlSum = '{0:.2f}'.format(sh['D5'].value)
sNm = sh['E5'].value.strip()
sId = sh['F5'].value.strip()
GrpHdr = etree.SubElement(CstmrCdtTrfInitn, "GrpHdr")
MsgId = etree.SubElement(GrpHdr, "MsgId")
MsgId.text = sMsgId
CreDtTm = etree.SubElement(GrpHdr, "CreDtTm")
CreDtTm.text = sCreDtTm
NbOfTxs = etree.SubElement(GrpHdr, "NbOfTxs")
NbOfTxs.text = sNbOfTxs
CtrlSum = etree.SubElement(GrpHdr, "CtrlSum")
CtrlSum.text = sCtrlSum
InitgPty = etree.SubElement(GrpHdr, "InitgPty")
Nm = etree.SubElement(InitgPty, "Nm")
Nm.text = sNm
Id1 = etree.SubElement(InitgPty, "Id")
OrgId = etree.SubElement(Id1, "OrgId")
Othr = etree.SubElement(OrgId, "Othr")
Id2 = etree.SubElement(Othr, "Id")
Id2.text = sId
return CstmrCdtTrfInitn